@fjader/svelte 0.2.2 → 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.
Files changed (118) hide show
  1. package/dist/components/axes/BandAxisTicks.svelte +63 -24
  2. package/dist/components/axes/BandAxisTicks.svelte.d.ts +5 -5
  3. package/dist/components/charts/ChartContainer.svelte +2 -1
  4. package/dist/components/guides/legend/ContinuousColorLegend.svelte +2 -1
  5. package/dist/components/guides/legend/OrdinalColorLegend.svelte +92 -34
  6. package/dist/components/marks/AreaMarkX.svelte +19 -6
  7. package/dist/components/marks/AreaMarkY.svelte +19 -6
  8. package/dist/components/marks/BarMarkX.svelte +3 -0
  9. package/dist/components/marks/BarMarkY.svelte +3 -0
  10. package/dist/components/marks/DotsMark.svelte +3 -0
  11. package/dist/components/marks/LineMark.svelte +3 -0
  12. package/dist/components/marks/ParallelCoordinatesMark.svelte +6 -0
  13. package/dist/components/marks/ParallelCoordinatesMark.svelte.d.ts +3 -0
  14. package/dist/components/marks/RectMark.svelte +3 -0
  15. package/dist/components/marks/RectXMark.svelte +3 -0
  16. package/dist/components/marks/RectYMark.svelte +3 -0
  17. package/dist/components/marks/RuleMarkX.svelte +10 -2
  18. package/dist/components/marks/RuleMarkY.svelte +10 -2
  19. package/dist/components/marks/TextMark.svelte +68 -4
  20. package/dist/components/marks/lifecycle/MarkLifecycleV2.svelte.d.ts +15 -1
  21. package/dist/components/marks/lifecycle/MarkLifecycleV2.svelte.js +116 -27
  22. package/dist/components/marks/lifecycle/domain-queries.js +13 -11
  23. package/dist/components/marks/lifecycle/transform-scale-key.d.ts +16 -0
  24. package/dist/components/marks/lifecycle/transform-scale-key.js +27 -0
  25. package/dist/components/marks/utils/area-outline.d.ts +24 -0
  26. package/dist/components/marks/utils/area-outline.js +10 -0
  27. package/dist/components/marks/utils/reactive-config.d.ts +1 -1
  28. package/dist/components/marks/utils/reactive-config.js +3 -0
  29. package/dist/components/rendering/Areas.svelte +30 -6
  30. package/dist/components/rendering/Areas.svelte.d.ts +2 -0
  31. package/dist/components/rendering/BaseGPURenderer.svelte.js +4 -1
  32. package/dist/components/rendering/Dots.svelte +24 -3
  33. package/dist/components/rendering/Dots.svelte.d.ts +2 -0
  34. package/dist/components/rendering/GridLine.svelte +2 -1
  35. package/dist/components/rendering/Lines.svelte +49 -7
  36. package/dist/components/rendering/Lines.svelte.d.ts +9 -0
  37. package/dist/components/rendering/ParallelLines.svelte +26 -7
  38. package/dist/components/rendering/ParallelLines.svelte.d.ts +2 -0
  39. package/dist/components/rendering/Rects.svelte +20 -5
  40. package/dist/components/rendering/Rects.svelte.d.ts +2 -0
  41. package/dist/components/rendering/RuleX.svelte +20 -5
  42. package/dist/components/rendering/RuleX.svelte.d.ts +2 -0
  43. package/dist/components/rendering/RuleY.svelte +20 -5
  44. package/dist/components/rendering/RuleY.svelte.d.ts +2 -0
  45. package/dist/components/rendering/activity-reads.d.ts +93 -0
  46. package/dist/components/rendering/activity-reads.js +135 -0
  47. package/dist/components/rendering/uniform-sizes.d.ts +26 -0
  48. package/dist/components/rendering/uniform-sizes.js +28 -0
  49. package/dist/config/ChartConfig.svelte.d.ts +2 -0
  50. package/dist/gpu/ColorScaleShaderLib.d.ts +13 -1
  51. package/dist/gpu/ColorScaleShaderLib.js +56 -5
  52. package/dist/gpu/GPUCoordinator.svelte.d.ts +8 -0
  53. package/dist/gpu/GPUCoordinator.svelte.js +68 -32
  54. package/dist/gpu/ResolveShader.d.ts +2 -2
  55. package/dist/gpu/ResolveShader.js +2 -2
  56. package/dist/gpu/ScaleTemplateSystem.d.ts +19 -0
  57. package/dist/gpu/ScaleTemplateSystem.js +86 -7
  58. package/dist/gpu/SizeScaleShaderLib.d.ts +4 -4
  59. package/dist/gpu/SizeScaleShaderLib.js +4 -4
  60. package/dist/gpu/identity-slot-packing.d.ts +157 -40
  61. package/dist/gpu/identity-slot-packing.js +262 -62
  62. package/dist/gpu/tracked-scales.d.ts +14 -0
  63. package/dist/gpu/tracked-scales.js +18 -0
  64. package/dist/index.js +1 -0
  65. package/dist/interactors/BandLabelToggle.svelte +5 -9
  66. package/dist/interactors/BandLabelToggle.svelte.d.ts +3 -3
  67. package/dist/interactors/DerivedInterval1D.svelte +26 -54
  68. package/dist/interactors/Interval1D.svelte +2 -1
  69. package/dist/interactors/Interval2D.svelte +2 -1
  70. package/dist/interactors/ParallelAxisSource.svelte +4 -4
  71. package/dist/interactors/ParallelAxisSource.svelte.d.ts +2 -1
  72. package/dist/interactors/ParallelBrushes.svelte +8 -9
  73. package/dist/interactors/ParallelBrushes.svelte.d.ts +3 -3
  74. package/dist/interactors/Toggle.svelte +17 -36
  75. package/dist/interactors/Toggle.svelte.d.ts +4 -3
  76. package/dist/interactors/Tooltip.svelte +2 -1
  77. package/dist/interactors/TooltipManager.svelte.js +8 -3
  78. package/dist/interactors/relay-clauses.d.ts +23 -0
  79. package/dist/interactors/relay-clauses.js +60 -0
  80. package/dist/query-engine/CrossfilterSelection.svelte.d.ts +88 -15
  81. package/dist/query-engine/CrossfilterSelection.svelte.js +94 -20
  82. package/dist/query-engine/FieldActivity.svelte.d.ts +79 -0
  83. package/dist/query-engine/FieldActivity.svelte.js +207 -0
  84. package/dist/query-engine/FilterGroup.svelte.d.ts +53 -29
  85. package/dist/query-engine/FilterGroup.svelte.js +181 -122
  86. package/dist/query-engine/FilterGroupRegistry.svelte.d.ts +42 -0
  87. package/dist/query-engine/FilterGroupRegistry.svelte.js +103 -1
  88. package/dist/query-engine/MarkHandle.svelte.d.ts +9 -1
  89. package/dist/query-engine/MarkHandle.svelte.js +12 -2
  90. package/dist/query-engine/QueryEngine.d.ts +52 -16
  91. package/dist/query-engine/QueryEngine.js +90 -28
  92. package/dist/query-engine/categorical-identity.d.ts +99 -27
  93. package/dist/query-engine/categorical-identity.js +210 -51
  94. package/dist/query-engine/index.d.ts +2 -1
  95. package/dist/query-engine/index.js +1 -0
  96. package/dist/query-engine/preagg/Preagg.d.ts +1 -1
  97. package/dist/query-engine/preagg/Preagg.js +11 -9
  98. package/dist/query-engine/preagg/plan.d.ts +9 -5
  99. package/dist/query-engine/preagg/plan.js +13 -11
  100. package/dist/query-engine/toggle-predicate.js +12 -1
  101. package/dist/scales/ActivityTable.d.ts +99 -0
  102. package/dist/scales/ActivityTable.js +211 -0
  103. package/dist/scales/BandScaleState.svelte.d.ts +28 -7
  104. package/dist/scales/BandScaleState.svelte.js +55 -20
  105. package/dist/scales/ChartScales.svelte.d.ts +2 -2
  106. package/dist/scales/ColorScaleState.svelte.d.ts +36 -8
  107. package/dist/scales/ColorScaleState.svelte.js +118 -24
  108. package/dist/scales/DomainResolver.d.ts +2 -0
  109. package/dist/scales/DomainResolver.js +15 -8
  110. package/dist/scales/ScaleRegistry.svelte.d.ts +9 -0
  111. package/dist/scales/ScaleRegistry.svelte.js +22 -1
  112. package/dist/scales/TimeScaleState.svelte.js +7 -1
  113. package/dist/spec/index.js +1 -0
  114. package/dist/spec/types.d.ts +2 -0
  115. package/dist/transforms/index.d.ts +27 -0
  116. package/dist/transforms/index.js +97 -3
  117. package/dist/types.d.ts +30 -0
  118. package/package.json +1 -1
@@ -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_FILL_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 rectRendererDefinition = {
9
11
  x: { concept: 'extent', channels: ['x0', 'x1'] },
@@ -30,7 +32,7 @@
30
32
  COLOR_MODE: 0,
31
33
  COLOR_SCALE_INDEX: 4,
32
34
  INSET: 8,
33
- _PAD0: 12,
35
+ GROUP_INDEX: 12,
34
36
  CONSTANT_COLOR: 16,
35
37
  DX: 32,
36
38
  DY: 36
@@ -49,6 +51,8 @@
49
51
  zIndex?: number;
50
52
  colorMode?: number;
51
53
  colorScaleIndex?: number;
54
+ /** The page-wide index of this mark's filter group: which run of a field's activity block it reads. */
55
+ groupIndex?: number;
52
56
  constantColor?: [number, number, number, number];
53
57
  inset?: number;
54
58
  dx?: number;
@@ -78,6 +82,7 @@
78
82
  zIndex = 0,
79
83
  colorMode = 0,
80
84
  colorScaleIndex = 0,
85
+ groupIndex = 0,
81
86
  constantColor = DEFAULT_FILL_COLOR,
82
87
  inset = 0,
83
88
  dx = 0,
@@ -119,13 +124,16 @@
119
124
 
120
125
  const rectVertexCount = 6;
121
126
 
127
+ /** The selection activities this shader reads; routing reads the same table. */
128
+ const ACTIVITY_READS = activityReads('rects');
129
+
122
130
  const rectsShaderCode = `
123
131
  // MarkConstants uniform for per-mark configuration
124
132
  struct MarkConstants {
125
133
  colorMode: u32, // 0 = constant, 1 = scale
126
134
  colorScaleIndex: u32, // Index into colorConfigs
127
135
  inset: f32, // Pixel inset on all sides
128
- _pad0: u32,
136
+ groupIndex: u32, // Page-wide filter group index, for activity reads
129
137
  constantColor: vec4<f32>, // Used when colorMode = 0
130
138
  dx: f32, // Horizontal pixel offset
131
139
  dy: f32, // Vertical pixel offset
@@ -182,7 +190,14 @@
182
190
  var output: VertexOutput;
183
191
  output.position = vec4<f32>(normalizedPos.x, -normalizedPos.y, 0.0, 1.0);
184
192
  output.colorValue = colorValue;
185
- output.alpha = alpha;
193
+ output.alpha = alpha * selectionDim(
194
+ ${markActivityWGSL(ACTIVITY_READS, {
195
+ color: 'markConstants.colorMode, markConstants.colorScaleIndex, colorValue',
196
+ x: 'x0',
197
+ y: 'y0',
198
+ group: 'markConstants.groupIndex'
199
+ })}
200
+ );
186
201
  return output;
187
202
  }
188
203
 
@@ -295,14 +310,14 @@
295
310
  // re-fires once data loads.
296
311
  if (colorMode === 1 && !colorValueData) return;
297
312
 
298
- const buffer = new ArrayBuffer(48);
313
+ const buffer = new ArrayBuffer(UNIFORM_BYTES.rectsMarkConstants);
299
314
  const view = new DataView(buffer);
300
315
  const LE = true;
301
316
 
302
317
  view.setUint32(UNIFORM_OFFSETS.COLOR_MODE, colorMode, LE);
303
318
  view.setUint32(UNIFORM_OFFSETS.COLOR_SCALE_INDEX, colorScaleIndex, LE);
319
+ view.setUint32(UNIFORM_OFFSETS.GROUP_INDEX, groupIndex, LE);
304
320
  view.setFloat32(UNIFORM_OFFSETS.INSET, inset, LE);
305
- // padding at 12
306
321
  view.setFloat32(UNIFORM_OFFSETS.CONSTANT_COLOR + 0, constantColor[0], LE);
307
322
  view.setFloat32(UNIFORM_OFFSETS.CONSTANT_COLOR + 4, constantColor[1], LE);
308
323
  view.setFloat32(UNIFORM_OFFSETS.CONSTANT_COLOR + 8, constantColor[2], LE);
@@ -10,6 +10,8 @@ interface Props {
10
10
  zIndex?: number;
11
11
  colorMode?: number;
12
12
  colorScaleIndex?: number;
13
+ /** The page-wide index of this mark's filter group: which run of a field's activity block it reads. */
14
+ groupIndex?: number;
13
15
  constantColor?: [number, number, number, number];
14
16
  inset?: number;
15
17
  dx?: number;
@@ -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_FILL_COLOR } from '../../scales/color-utils';
6
7
  import type { ChartContext, WebGPUCanvasContext } from '../../types';
8
+ import { activityReads, markActivityWGSL } from './activity-reads';
7
9
 
8
10
  // Named Bindings — stable indices regardless of hasExtent
9
11
  const BINDINGS = {
@@ -23,7 +25,7 @@
23
25
  COLOR_MODE: 0,
24
26
  COLOR_SCALE_INDEX: 4,
25
27
  STROKE_WIDTH: 8,
26
- _PAD0: 12,
28
+ GROUP_INDEX: 12,
27
29
  CONSTANT_COLOR: 16,
28
30
  _PAD1: 32,
29
31
  _PAD2: 36,
@@ -42,6 +44,8 @@
42
44
  zIndex?: number;
43
45
  colorMode?: number;
44
46
  colorScaleIndex?: number;
47
+ /** The page-wide index of this mark's filter group: which run of a field's activity block it reads. */
48
+ groupIndex?: number;
45
49
  constantColor?: [number, number, number, number];
46
50
  strokeWidth?: number;
47
51
  markId?: string;
@@ -58,6 +62,7 @@
58
62
  zIndex = 0,
59
63
  colorMode = 0,
60
64
  colorScaleIndex = 0,
65
+ groupIndex = 0,
61
66
  constantColor = DEFAULT_FILL_COLOR,
62
67
  strokeWidth = 1,
63
68
  markId = ''
@@ -113,12 +118,15 @@
113
118
  const initializationError = $derived(host.error);
114
119
  let lineGeometryBuffer: GPUBuffer;
115
120
 
121
+ /** The selection activities this shader reads; routing reads the same table. */
122
+ const ACTIVITY_READS = activityReads('rulex', hasExtent);
123
+
116
124
  const ruleXShaderCode = `
117
125
  struct MarkConstants {
118
126
  colorMode: u32,
119
127
  colorScaleIndex: u32,
120
128
  strokeWidth: f32,
121
- _pad0: u32,
129
+ groupIndex: u32,
122
130
  constantColor: vec4<f32>,
123
131
  _pad1: f32,
124
132
  _pad2: f32,
@@ -173,7 +181,14 @@
173
181
  var output: VertexOutput;
174
182
  output.position = vec4<f32>(normalizedPos.x, -normalizedPos.y, 0.0, 1.0);
175
183
  output.colorValue = colorValue;
176
- output.alpha = alpha;
184
+ output.alpha = alpha * selectionDim(
185
+ ${markActivityWGSL(ACTIVITY_READS, {
186
+ color: 'markConstants.colorMode, markConstants.colorScaleIndex, colorValue',
187
+ x: 'x',
188
+ y: 'y0',
189
+ group: 'markConstants.groupIndex'
190
+ })}
191
+ );
177
192
  return output;
178
193
  }
179
194
 
@@ -266,14 +281,14 @@
266
281
  if (renderer?.resourcesCreated) {
267
282
  if (colorMode === 1 && !colorValueData) return;
268
283
 
269
- const buffer = new ArrayBuffer(48);
284
+ const buffer = new ArrayBuffer(UNIFORM_BYTES.ruleXMarkConstants);
270
285
  const view = new DataView(buffer);
271
286
  const LE = true;
272
287
 
273
288
  view.setUint32(UNIFORM_OFFSETS.COLOR_MODE, colorMode, LE);
274
289
  view.setUint32(UNIFORM_OFFSETS.COLOR_SCALE_INDEX, colorScaleIndex, LE);
290
+ view.setUint32(UNIFORM_OFFSETS.GROUP_INDEX, groupIndex, LE);
275
291
  view.setFloat32(UNIFORM_OFFSETS.STROKE_WIDTH, strokeWidth, LE);
276
- // padding at 12
277
292
  view.setFloat32(UNIFORM_OFFSETS.CONSTANT_COLOR + 0, constantColor[0], LE);
278
293
  view.setFloat32(UNIFORM_OFFSETS.CONSTANT_COLOR + 4, constantColor[1], LE);
279
294
  view.setFloat32(UNIFORM_OFFSETS.CONSTANT_COLOR + 8, constantColor[2], 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
  strokeWidth?: number;
14
16
  markId?: string;
@@ -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_FILL_COLOR } from '../../scales/color-utils';
6
7
  import type { ChartContext, WebGPUCanvasContext } from '../../types';
8
+ import { activityReads, markActivityWGSL } from './activity-reads';
7
9
 
8
10
  // Named Bindings — stable indices regardless of hasExtent
9
11
  const BINDINGS = {
@@ -23,7 +25,7 @@
23
25
  COLOR_MODE: 0,
24
26
  COLOR_SCALE_INDEX: 4,
25
27
  STROKE_WIDTH: 8,
26
- _PAD0: 12,
28
+ GROUP_INDEX: 12,
27
29
  CONSTANT_COLOR: 16,
28
30
  _PAD1: 32,
29
31
  _PAD2: 36,
@@ -42,6 +44,8 @@
42
44
  zIndex?: number;
43
45
  colorMode?: number;
44
46
  colorScaleIndex?: number;
47
+ /** The page-wide index of this mark's filter group: which run of a field's activity block it reads. */
48
+ groupIndex?: number;
45
49
  constantColor?: [number, number, number, number];
46
50
  strokeWidth?: number;
47
51
  markId?: string;
@@ -58,6 +62,7 @@
58
62
  zIndex = 0,
59
63
  colorMode = 0,
60
64
  colorScaleIndex = 0,
65
+ groupIndex = 0,
61
66
  constantColor = DEFAULT_FILL_COLOR,
62
67
  strokeWidth = 1,
63
68
  markId = ''
@@ -112,12 +117,15 @@
112
117
  const initializationError = $derived(host.error);
113
118
  let lineGeometryBuffer: GPUBuffer;
114
119
 
120
+ /** The selection activities this shader reads; routing reads the same table. */
121
+ const ACTIVITY_READS = activityReads('ruley', hasExtent);
122
+
115
123
  const ruleYShaderCode = `
116
124
  struct MarkConstants {
117
125
  colorMode: u32,
118
126
  colorScaleIndex: u32,
119
127
  strokeWidth: f32,
120
- _pad0: u32,
128
+ groupIndex: u32,
121
129
  constantColor: vec4<f32>,
122
130
  _pad1: f32,
123
131
  _pad2: f32,
@@ -172,7 +180,14 @@
172
180
  var output: VertexOutput;
173
181
  output.position = vec4<f32>(normalizedPos.x, -normalizedPos.y, 0.0, 1.0);
174
182
  output.colorValue = colorValue;
175
- output.alpha = alpha;
183
+ output.alpha = alpha * selectionDim(
184
+ ${markActivityWGSL(ACTIVITY_READS, {
185
+ color: 'markConstants.colorMode, markConstants.colorScaleIndex, colorValue',
186
+ x: 'x0',
187
+ y: 'y',
188
+ group: 'markConstants.groupIndex'
189
+ })}
190
+ );
176
191
  return output;
177
192
  }
178
193
 
@@ -265,14 +280,14 @@
265
280
  if (renderer?.resourcesCreated) {
266
281
  if (colorMode === 1 && !colorValueData) return;
267
282
 
268
- const buffer = new ArrayBuffer(48);
283
+ const buffer = new ArrayBuffer(UNIFORM_BYTES.ruleYMarkConstants);
269
284
  const view = new DataView(buffer);
270
285
  const LE = true;
271
286
 
272
287
  view.setUint32(UNIFORM_OFFSETS.COLOR_MODE, colorMode, LE);
273
288
  view.setUint32(UNIFORM_OFFSETS.COLOR_SCALE_INDEX, colorScaleIndex, LE);
289
+ view.setUint32(UNIFORM_OFFSETS.GROUP_INDEX, groupIndex, LE);
274
290
  view.setFloat32(UNIFORM_OFFSETS.STROKE_WIDTH, strokeWidth, LE);
275
- // padding at 12
276
291
  view.setFloat32(UNIFORM_OFFSETS.CONSTANT_COLOR + 0, constantColor[0], LE);
277
292
  view.setFloat32(UNIFORM_OFFSETS.CONSTANT_COLOR + 4, constantColor[1], LE);
278
293
  view.setFloat32(UNIFORM_OFFSETS.CONSTANT_COLOR + 8, constantColor[2], 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
  strokeWidth?: number;
14
16
  markId?: string;
@@ -0,0 +1,93 @@
1
+ /**
2
+ * Which selection activities a mark reads: its ordinal colour, its band x,
3
+ * its band y. One answer per renderer, used twice:
4
+ *
5
+ * - The renderer builds its shader's `markActivity` call from it, so a read
6
+ * the table does not list is passed as 1.0 and cannot happen.
7
+ * - MarkLifecycleV2 turns it into the mark's `reads`, the identity keys of the
8
+ * fields it dims by, which decide whether a categorical selection on a field
9
+ * dims the mark or filters it.
10
+ *
11
+ * The two must agree exactly. A mark routed to dim on a field its shader does
12
+ * not read would be neither dimmed nor filtered; one whose shader reads a field
13
+ * routing missed would be filtered and dimmed at once. Deriving both from this
14
+ * table is what keeps them together. One case they still disagree on: a field
15
+ * whose activity block did not fit in the identity slot buffer (see the known
16
+ * limit in identity-slot-packing.ts).
17
+ *
18
+ * A function of the config, not a constant per renderer, because a rule's
19
+ * answer depends on a prop: a rule without an extent spans the whole cross
20
+ * axis, belongs to no category on it, and must not dim by it.
21
+ */
22
+ import { NO_GROUP } from '../../gpu/identity-slot-packing';
23
+ /** The renderers that read selection activity. Text reads it on the CPU. */
24
+ export type ActivityRenderer = 'dots' | 'rects' | 'rulex' | 'ruley' | 'lines' | 'areas' | 'parallel' | 'text';
25
+ export interface ActivityReads {
26
+ readonly color: boolean;
27
+ readonly x: boolean;
28
+ readonly y: boolean;
29
+ }
30
+ /**
31
+ * What a renderer reads. `hasExtent` is a rule's: RuleX with y1/y2, RuleY with
32
+ * x1/x2. Lines, areas and parallel coordinates dim by colour only: a line over
33
+ * a band axis spans its categories.
34
+ */
35
+ export declare function activityReads(renderer: ActivityRenderer, hasExtent?: boolean): ActivityReads;
36
+ /**
37
+ * Whether a rule spans an extent on its cross axis: both ends given, as a
38
+ * field or a constant. A rule mark builds its renderer's extent data from this
39
+ * and hands it to routing as `ruleExtent`, so the two cannot disagree; a
40
+ * constant end (y1={0}, or the y shorthand) counts like a field.
41
+ */
42
+ export declare function ruleHasExtent(lo: unknown, hi: unknown): boolean;
43
+ /** The renderer a mark type draws with, or null for one that reads no activity. */
44
+ export declare function rendererForMark(markType: string): ActivityRenderer | null;
45
+ /**
46
+ * A mark's reads: the identity keys of the fields whose activity it reads,
47
+ * sorted, from what its renderer reads and the identity key of each of its
48
+ * categorical scales (null for a channel with no such scale, or a scale with
49
+ * no identity: a field with no identity has no activity to dim by, so a
50
+ * selection on it filters the mark).
51
+ */
52
+ export declare function readsFor(reads: ActivityReads, keys: {
53
+ color: string | null;
54
+ x: string | null;
55
+ y: string | null;
56
+ }): readonly string[];
57
+ /** A mark that reads no activity. Shared, so its reads never look changed. */
58
+ export declare const NO_READS: readonly string[];
59
+ /**
60
+ * How a mark responds to selections in its filter group (the `selectionResponse`
61
+ * prop). 'auto', the default: a categorical selection on a field the mark draws
62
+ * by (its band axes, its ordinal colour) dims it, and any other selection
63
+ * filters it. 'filter': every selection filters it, even on a field it draws
64
+ * by, so its aggregates are recomputed for the subset (top N within a region).
65
+ */
66
+ export type SelectionResponse = 'auto' | 'filter';
67
+ export { NO_GROUP };
68
+ /** A mark's reads under its selection response: none for 'filter', which never dims. */
69
+ export declare function readsForResponse(response: SelectionResponse | undefined, reads: readonly string[]): readonly string[];
70
+ /** The group index a mark's shader reads activity with: NO_GROUP for 'filter'. */
71
+ export declare function groupIndexForResponse(response: SelectionResponse | undefined, groupIndex: number): number;
72
+ /**
73
+ * The WGSL activity of a renderer that reads colour only (lines, areas,
74
+ * parallel coordinates): `markColorActivity(...)`, or 1.0 if the table says
75
+ * it reads nothing. Throws for a renderer that reads a band axis, whose call
76
+ * is markActivityWGSL.
77
+ */
78
+ export declare function colorActivityWGSL(reads: ActivityReads, args: {
79
+ color: string;
80
+ group: string;
81
+ }): string;
82
+ /**
83
+ * The WGSL `markActivity(...)` call for `reads`: each activity the renderer
84
+ * reads, and 1.0 for each it does not. `color` is markColorActivity's first
85
+ * three arguments; `x` and `y` are the band channel values; `group` the mark's
86
+ * group index.
87
+ */
88
+ export declare function markActivityWGSL(reads: ActivityReads, args: {
89
+ color: string;
90
+ x: string;
91
+ y: string;
92
+ group: string;
93
+ }): string;
@@ -0,0 +1,135 @@
1
+ /**
2
+ * Which selection activities a mark reads: its ordinal colour, its band x,
3
+ * its band y. One answer per renderer, used twice:
4
+ *
5
+ * - The renderer builds its shader's `markActivity` call from it, so a read
6
+ * the table does not list is passed as 1.0 and cannot happen.
7
+ * - MarkLifecycleV2 turns it into the mark's `reads`, the identity keys of the
8
+ * fields it dims by, which decide whether a categorical selection on a field
9
+ * dims the mark or filters it.
10
+ *
11
+ * The two must agree exactly. A mark routed to dim on a field its shader does
12
+ * not read would be neither dimmed nor filtered; one whose shader reads a field
13
+ * routing missed would be filtered and dimmed at once. Deriving both from this
14
+ * table is what keeps them together. One case they still disagree on: a field
15
+ * whose activity block did not fit in the identity slot buffer (see the known
16
+ * limit in identity-slot-packing.ts).
17
+ *
18
+ * A function of the config, not a constant per renderer, because a rule's
19
+ * answer depends on a prop: a rule without an extent spans the whole cross
20
+ * axis, belongs to no category on it, and must not dim by it.
21
+ */
22
+ import { NO_GROUP } from '../../gpu/identity-slot-packing';
23
+ const ALL = { color: true, x: true, y: true };
24
+ const COLOR_ONLY = { color: true, x: false, y: false };
25
+ /**
26
+ * What a renderer reads. `hasExtent` is a rule's: RuleX with y1/y2, RuleY with
27
+ * x1/x2. Lines, areas and parallel coordinates dim by colour only: a line over
28
+ * a band axis spans its categories.
29
+ */
30
+ export function activityReads(renderer, hasExtent = false) {
31
+ switch (renderer) {
32
+ case 'dots':
33
+ case 'rects':
34
+ case 'text':
35
+ return ALL;
36
+ case 'rulex':
37
+ return { color: true, x: true, y: hasExtent };
38
+ case 'ruley':
39
+ return { color: true, x: hasExtent, y: true };
40
+ case 'lines':
41
+ case 'areas':
42
+ case 'parallel':
43
+ return COLOR_ONLY;
44
+ }
45
+ }
46
+ /**
47
+ * Whether a rule spans an extent on its cross axis: both ends given, as a
48
+ * field or a constant. A rule mark builds its renderer's extent data from this
49
+ * and hands it to routing as `ruleExtent`, so the two cannot disagree; a
50
+ * constant end (y1={0}, or the y shorthand) counts like a field.
51
+ */
52
+ export function ruleHasExtent(lo, hi) {
53
+ return lo !== undefined && hi !== undefined;
54
+ }
55
+ /** The renderer a mark type draws with, or null for one that reads no activity. */
56
+ export function rendererForMark(markType) {
57
+ switch (markType) {
58
+ case 'dot':
59
+ return 'dots';
60
+ case 'bary':
61
+ case 'barx':
62
+ case 'rect':
63
+ case 'rectx':
64
+ case 'recty':
65
+ return 'rects';
66
+ case 'rulex':
67
+ return 'rulex';
68
+ case 'ruley':
69
+ return 'ruley';
70
+ case 'line':
71
+ return 'lines';
72
+ case 'areax':
73
+ case 'areay':
74
+ return 'areas';
75
+ case 'parallel':
76
+ return 'parallel';
77
+ case 'text':
78
+ return 'text';
79
+ default:
80
+ return null;
81
+ }
82
+ }
83
+ /**
84
+ * A mark's reads: the identity keys of the fields whose activity it reads,
85
+ * sorted, from what its renderer reads and the identity key of each of its
86
+ * categorical scales (null for a channel with no such scale, or a scale with
87
+ * no identity: a field with no identity has no activity to dim by, so a
88
+ * selection on it filters the mark).
89
+ */
90
+ export function readsFor(reads, keys) {
91
+ const out = [];
92
+ if (reads.color && keys.color)
93
+ out.push(keys.color);
94
+ if (reads.x && keys.x)
95
+ out.push(keys.x);
96
+ if (reads.y && keys.y)
97
+ out.push(keys.y);
98
+ if (out.length === 0)
99
+ return NO_READS;
100
+ return [...new Set(out)].sort();
101
+ }
102
+ /** A mark that reads no activity. Shared, so its reads never look changed. */
103
+ export const NO_READS = [];
104
+ export { NO_GROUP };
105
+ /** A mark's reads under its selection response: none for 'filter', which never dims. */
106
+ export function readsForResponse(response, reads) {
107
+ return response === 'filter' ? NO_READS : reads;
108
+ }
109
+ /** The group index a mark's shader reads activity with: NO_GROUP for 'filter'. */
110
+ export function groupIndexForResponse(response, groupIndex) {
111
+ return response === 'filter' ? NO_GROUP : groupIndex;
112
+ }
113
+ /**
114
+ * The WGSL activity of a renderer that reads colour only (lines, areas,
115
+ * parallel coordinates): `markColorActivity(...)`, or 1.0 if the table says
116
+ * it reads nothing. Throws for a renderer that reads a band axis, whose call
117
+ * is markActivityWGSL.
118
+ */
119
+ export function colorActivityWGSL(reads, args) {
120
+ if (reads.x || reads.y)
121
+ throw new Error('colorActivityWGSL: this renderer reads a band axis');
122
+ return reads.color ? `markColorActivity(${args.color}, ${args.group})` : '1.0';
123
+ }
124
+ /**
125
+ * The WGSL `markActivity(...)` call for `reads`: each activity the renderer
126
+ * reads, and 1.0 for each it does not. `color` is markColorActivity's first
127
+ * three arguments; `x` and `y` are the band channel values; `group` the mark's
128
+ * group index.
129
+ */
130
+ export function markActivityWGSL(reads, args) {
131
+ const color = reads.color ? `markColorActivity(${args.color}, ${args.group})` : '1.0';
132
+ const x = reads.x ? `bandActivityX(${args.x}, ${args.group})` : '1.0';
133
+ const y = reads.y ? `bandActivityY(${args.y}, ${args.group})` : '1.0';
134
+ return `markActivity(${color}, ${x}, ${y})`;
135
+ }
@@ -0,0 +1,26 @@
1
+ /**
2
+ * The byte size of every renderer's uniform struct, as WGSL lays it out.
3
+ *
4
+ * Renderers allocate their uniform buffers from these, and BaseGPURenderer
5
+ * sizes its stand-in from the largest: a binding not yet written is bound to
6
+ * the stand-in, and WebGPU rejects a draw whose uniform buffer is smaller than
7
+ * the struct it declares, which loses every chart's frame in that submit. A
8
+ * comment saying "must fit the largest struct" went stale the first time a
9
+ * struct grew, so uniform-sizes.test.ts derives each size from the shader
10
+ * source and fails when one drifts.
11
+ */
12
+ /** Axes a parallel coordinates chart can hold: its axis arrays are sized for it. */
13
+ export declare const PARALLEL_MAX_AXES = 16;
14
+ export declare const UNIFORM_BYTES: {
15
+ readonly dotsMarkConstants: 48;
16
+ readonly rectsMarkConstants: 48;
17
+ readonly ruleXMarkConstants: 48;
18
+ readonly ruleYMarkConstants: 48;
19
+ readonly linesMarkConstants: 64;
20
+ readonly areasMarkConstants: 64;
21
+ readonly gridLineRuleConstants: 32;
22
+ readonly parallelConfig: 176;
23
+ readonly parallelAxisScales: number;
24
+ };
25
+ /** The stand-in uniform buffer: as large as the largest struct any renderer binds. */
26
+ export declare const UNIFORM_STAND_IN_BYTES: number;
@@ -0,0 +1,28 @@
1
+ /**
2
+ * The byte size of every renderer's uniform struct, as WGSL lays it out.
3
+ *
4
+ * Renderers allocate their uniform buffers from these, and BaseGPURenderer
5
+ * sizes its stand-in from the largest: a binding not yet written is bound to
6
+ * the stand-in, and WebGPU rejects a draw whose uniform buffer is smaller than
7
+ * the struct it declares, which loses every chart's frame in that submit. A
8
+ * comment saying "must fit the largest struct" went stale the first time a
9
+ * struct grew, so uniform-sizes.test.ts derives each size from the shader
10
+ * source and fails when one drifts.
11
+ */
12
+ /** Axes a parallel coordinates chart can hold: its axis arrays are sized for it. */
13
+ export const PARALLEL_MAX_AXES = 16;
14
+ /** One ScaleParam: four f32. */
15
+ const SCALE_PARAM_BYTES = 16;
16
+ export const UNIFORM_BYTES = {
17
+ dotsMarkConstants: 48,
18
+ rectsMarkConstants: 48,
19
+ ruleXMarkConstants: 48,
20
+ ruleYMarkConstants: 48,
21
+ linesMarkConstants: 64,
22
+ areasMarkConstants: 64,
23
+ gridLineRuleConstants: 32,
24
+ parallelConfig: 176,
25
+ parallelAxisScales: PARALLEL_MAX_AXES * SCALE_PARAM_BYTES
26
+ };
27
+ /** The stand-in uniform buffer: as large as the largest struct any renderer binds. */
28
+ export const UNIFORM_STAND_IN_BYTES = Math.max(...Object.values(UNIFORM_BYTES));
@@ -1,3 +1,4 @@
1
+ import type { SelectionResponse } from '../components/rendering/activity-reads';
1
2
  import type { FieldRef, LODConfig, SortConfig, ColorScheme, CurveType, Transform } from '../types';
2
3
  import type { MarkLODSurface } from '../lod/rung-types';
3
4
  export interface MarkEntry {
@@ -23,6 +24,7 @@ export interface MarkEntry {
23
24
  scheme?: ColorScheme;
24
25
  transforms?: Transform[];
25
26
  selection?: string;
27
+ selectionResponse?: SelectionResponse;
26
28
  limit?: number;
27
29
  preferredXScaleType?: string | null;
28
30
  preferredYScaleType?: string | null;
@@ -15,6 +15,18 @@ export declare const COLOR_CONFIG_BYTES = 48;
15
15
  export declare const COLOR_CONFIG_SLOT_BASE_OFFSET = 32;
16
16
  /** `slotBase` meaning "this channel already carries a palette index". */
17
17
  export declare const NO_SLOT_TABLE = 4294967295;
18
+ /**
19
+ * The rank of a category an ordinal colour scale draws in the null colour: it
20
+ * takes no palette slot, so it reads as missing data and shifts no category's
21
+ * colour. The NULL category and the Other buckets. -1, by contrast, is a category
22
+ * outside this scale's domain, which draws transparent.
23
+ */
24
+ export declare const NULL_COLOR_RANK = -2;
25
+ /**
26
+ * How many colour configs the buffer holds. A uniform array has a fixed length
27
+ * in WGSL, so the shader declaration and the buffer size both read this.
28
+ */
29
+ export declare const MAX_COLOR_SLOTS = 64;
18
30
  export declare const COLOR_SCALE_STRUCTS = "\nstruct ColorConfig {\n domainMin: f32, // byte 0 \u2014 pre-transformed for non-linear types\n domainMax: f32, // byte 4 \u2014 pre-transformed for non-linear types\n rampOffset: u32, // byte 8\n paletteOffset: u32, // byte 12\n paletteSize: u32, // byte 16\n scaleType: u32, // byte 20 \u2014 scheme rendering type (ordinal/sequential/diverging/cyclical)\n transformType: u32, // byte 24 \u2014 normalization transform (linear/log/symlog/sqrt/pow)\n transformParam: f32, // byte 28 \u2014 symlog constant C, or pow exponent\n slotBase: u32, // byte 32 \u2014 base into identitySlots, or NO_SLOT_TABLE\n _pad0: u32, // byte 36\n _pad1: u32, // byte 40\n _pad2: u32, // byte 44\n}\n\n/**\n * slotBase when this scale's channel already carries a palette index rather\n * than an identity id. Distinct from base 0, which is a real block \u2014 the\n * permanently empty one \u2014 so it cannot be confused with \"not placed\".\n */\nconst NO_SLOT_TABLE: u32 = 0xFFFFFFFFu;\n\nconst COLOR_SCALE_ORDINAL: u32 = 0u;\nconst COLOR_SCALE_SEQUENTIAL: u32 = 1u;\nconst COLOR_SCALE_DIVERGING: u32 = 2u;\nconst COLOR_SCALE_CYCLICAL: u32 = 3u;\n\nconst COLOR_TRANSFORM_LINEAR: u32 = 0u;\nconst COLOR_TRANSFORM_LOG: u32 = 1u;\nconst COLOR_TRANSFORM_SYMLOG: u32 = 2u;\nconst COLOR_TRANSFORM_SQRT: u32 = 3u;\nconst COLOR_TRANSFORM_POW: u32 = 4u;\n";
19
- export declare const COLOR_SCALE_BINDINGS = "\n@group(2) @binding(1) var<storage, read> colorConfigs: array<ColorConfig>;\n@group(2) @binding(2) var<storage, read> colorRamps: array<vec4<f32>>;\n@group(2) @binding(3) var<storage, read> ordinalPalettes: array<vec4<f32>>;\n";
31
+ export declare const COLOR_SCALE_BINDINGS = "\n@group(2) @binding(1) var<uniform> colorConfigs: array<ColorConfig, 64>;\n@group(2) @binding(2) var<storage, read> colorRamps: array<vec4<f32>>;\n@group(2) @binding(3) var<storage, read> ordinalPalettes: array<vec4<f32>>;\n";
20
32
  export declare const COLOR_SCALE_FUNCTIONS: string;