@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.
Files changed (148) hide show
  1. package/README.md +2 -0
  2. package/dist/components/axes/AxisBottom.svelte +23 -6
  3. package/dist/components/axes/AxisBottom.svelte.d.ts +10 -0
  4. package/dist/components/axes/AxisLeft.svelte +77 -8
  5. package/dist/components/axes/AxisLeft.svelte.d.ts +17 -0
  6. package/dist/components/axes/BandAxisTicks.svelte +64 -25
  7. package/dist/components/axes/BandAxisTicks.svelte.d.ts +5 -5
  8. package/dist/components/axes/axis-title.d.ts +30 -0
  9. package/dist/components/axes/axis-title.js +77 -0
  10. package/dist/components/charts/ChartCell.svelte +1 -1
  11. package/dist/components/charts/ChartContainer.svelte +12 -3
  12. package/dist/components/charts/ChartContainer.svelte.d.ts +7 -0
  13. package/dist/components/guides/legend/ContinuousColorLegend.svelte +9 -2
  14. package/dist/components/guides/legend/OrdinalColorLegend.svelte +96 -35
  15. package/dist/components/marks/AreaMarkX.svelte +21 -6
  16. package/dist/components/marks/AreaMarkY.svelte +21 -6
  17. package/dist/components/marks/BarMarkX.svelte +16 -1
  18. package/dist/components/marks/BarMarkY.svelte +16 -1
  19. package/dist/components/marks/DotsMark.svelte +5 -0
  20. package/dist/components/marks/LineMark.svelte +8 -1
  21. package/dist/components/marks/ParallelCoordinatesMark.svelte +10 -0
  22. package/dist/components/marks/ParallelCoordinatesMark.svelte.d.ts +5 -0
  23. package/dist/components/marks/RectMark.svelte +5 -0
  24. package/dist/components/marks/RectXMark.svelte +5 -0
  25. package/dist/components/marks/RectYMark.svelte +5 -0
  26. package/dist/components/marks/RuleMarkX.svelte +12 -2
  27. package/dist/components/marks/RuleMarkY.svelte +12 -2
  28. package/dist/components/marks/TextMark.svelte +76 -13
  29. package/dist/components/marks/lifecycle/MarkLifecycleV2.svelte.d.ts +21 -2
  30. package/dist/components/marks/lifecycle/MarkLifecycleV2.svelte.js +300 -42
  31. package/dist/components/marks/lifecycle/domain-queries.d.ts +3 -1
  32. package/dist/components/marks/lifecycle/domain-queries.js +22 -15
  33. package/dist/components/marks/lifecycle/domain-registration.js +2 -6
  34. package/dist/components/marks/lifecycle/other-rows.svelte.d.ts +104 -0
  35. package/dist/components/marks/lifecycle/other-rows.svelte.js +193 -0
  36. package/dist/components/marks/lifecycle/other-split.d.ts +80 -0
  37. package/dist/components/marks/lifecycle/other-split.js +112 -0
  38. package/dist/components/marks/lifecycle/query-builder.d.ts +26 -1
  39. package/dist/components/marks/lifecycle/query-builder.js +25 -5
  40. package/dist/components/marks/lifecycle/transform-scale-key.d.ts +16 -0
  41. package/dist/components/marks/lifecycle/transform-scale-key.js +27 -0
  42. package/dist/components/marks/utils/area-outline.d.ts +24 -0
  43. package/dist/components/marks/utils/area-outline.js +10 -0
  44. package/dist/components/marks/utils/reactive-config.d.ts +1 -1
  45. package/dist/components/marks/utils/reactive-config.js +9 -0
  46. package/dist/components/marks/utils/tooltip-helpers.d.ts +4 -1
  47. package/dist/components/marks/utils/tooltip-helpers.js +2 -1
  48. package/dist/components/rendering/Areas.svelte +30 -6
  49. package/dist/components/rendering/Areas.svelte.d.ts +2 -0
  50. package/dist/components/rendering/BaseGPURenderer.svelte.d.ts +3 -1
  51. package/dist/components/rendering/BaseGPURenderer.svelte.js +8 -3
  52. package/dist/components/rendering/Dots.svelte +24 -3
  53. package/dist/components/rendering/Dots.svelte.d.ts +2 -0
  54. package/dist/components/rendering/GridLine.svelte +2 -1
  55. package/dist/components/rendering/Lines.svelte +49 -7
  56. package/dist/components/rendering/Lines.svelte.d.ts +9 -0
  57. package/dist/components/rendering/ParallelLines.svelte +26 -7
  58. package/dist/components/rendering/ParallelLines.svelte.d.ts +2 -0
  59. package/dist/components/rendering/Rects.svelte +129 -20
  60. package/dist/components/rendering/Rects.svelte.d.ts +9 -0
  61. package/dist/components/rendering/RuleX.svelte +20 -5
  62. package/dist/components/rendering/RuleX.svelte.d.ts +2 -0
  63. package/dist/components/rendering/RuleY.svelte +20 -5
  64. package/dist/components/rendering/RuleY.svelte.d.ts +2 -0
  65. package/dist/components/rendering/activity-reads.d.ts +111 -0
  66. package/dist/components/rendering/activity-reads.js +154 -0
  67. package/dist/components/rendering/uniform-sizes.d.ts +26 -0
  68. package/dist/components/rendering/uniform-sizes.js +28 -0
  69. package/dist/config/ChartConfig.svelte.d.ts +5 -1
  70. package/dist/gpu/ColorScaleShaderLib.d.ts +13 -1
  71. package/dist/gpu/ColorScaleShaderLib.js +56 -5
  72. package/dist/gpu/GPUCoordinator.svelte.d.ts +8 -0
  73. package/dist/gpu/GPUCoordinator.svelte.js +68 -32
  74. package/dist/gpu/MorphedChannels.d.ts +6 -32
  75. package/dist/gpu/MorphedChannels.js +45 -3
  76. package/dist/gpu/ResolveShader.d.ts +2 -2
  77. package/dist/gpu/ResolveShader.js +2 -2
  78. package/dist/gpu/ScaleTemplateSystem.d.ts +19 -0
  79. package/dist/gpu/ScaleTemplateSystem.js +111 -11
  80. package/dist/gpu/SizeScaleShaderLib.d.ts +4 -4
  81. package/dist/gpu/SizeScaleShaderLib.js +4 -4
  82. package/dist/gpu/identity-slot-packing.d.ts +157 -40
  83. package/dist/gpu/identity-slot-packing.js +262 -62
  84. package/dist/gpu/tracked-scales.d.ts +14 -0
  85. package/dist/gpu/tracked-scales.js +18 -0
  86. package/dist/index.d.ts +2 -0
  87. package/dist/index.js +4 -0
  88. package/dist/interactors/BandLabelToggle.svelte +5 -9
  89. package/dist/interactors/BandLabelToggle.svelte.d.ts +3 -3
  90. package/dist/interactors/DerivedInterval1D.svelte +26 -54
  91. package/dist/interactors/Interval1D.svelte +8 -2
  92. package/dist/interactors/Interval2D.svelte +8 -2
  93. package/dist/interactors/ParallelAxisSource.svelte +14 -5
  94. package/dist/interactors/ParallelAxisSource.svelte.d.ts +2 -1
  95. package/dist/interactors/ParallelBrushes.svelte +8 -9
  96. package/dist/interactors/ParallelBrushes.svelte.d.ts +3 -3
  97. package/dist/interactors/Toggle.svelte +48 -38
  98. package/dist/interactors/Toggle.svelte.d.ts +4 -3
  99. package/dist/interactors/Tooltip.svelte +140 -42
  100. package/dist/interactors/TooltipManager.svelte.d.ts +20 -5
  101. package/dist/interactors/TooltipManager.svelte.js +40 -10
  102. package/dist/interactors/other-tooltip.d.ts +16 -0
  103. package/dist/interactors/other-tooltip.js +22 -0
  104. package/dist/interactors/relay-clauses.d.ts +23 -0
  105. package/dist/interactors/relay-clauses.js +60 -0
  106. package/dist/query-engine/CrossfilterSelection.svelte.d.ts +88 -15
  107. package/dist/query-engine/CrossfilterSelection.svelte.js +94 -20
  108. package/dist/query-engine/FieldActivity.svelte.d.ts +128 -0
  109. package/dist/query-engine/FieldActivity.svelte.js +378 -0
  110. package/dist/query-engine/FilterGroup.svelte.d.ts +76 -30
  111. package/dist/query-engine/FilterGroup.svelte.js +223 -124
  112. package/dist/query-engine/FilterGroupRegistry.svelte.d.ts +73 -3
  113. package/dist/query-engine/FilterGroupRegistry.svelte.js +161 -12
  114. package/dist/query-engine/MarkHandle.svelte.d.ts +9 -1
  115. package/dist/query-engine/MarkHandle.svelte.js +12 -2
  116. package/dist/query-engine/QueryEngine.d.ts +52 -16
  117. package/dist/query-engine/QueryEngine.js +116 -34
  118. package/dist/query-engine/categorical-identity.d.ts +114 -27
  119. package/dist/query-engine/categorical-identity.js +225 -51
  120. package/dist/query-engine/index.d.ts +2 -1
  121. package/dist/query-engine/index.js +1 -0
  122. package/dist/query-engine/preagg/Preagg.d.ts +1 -1
  123. package/dist/query-engine/preagg/Preagg.js +11 -9
  124. package/dist/query-engine/preagg/plan.d.ts +14 -5
  125. package/dist/query-engine/preagg/plan.js +44 -18
  126. package/dist/query-engine/toggle-predicate.d.ts +21 -0
  127. package/dist/query-engine/toggle-predicate.js +41 -2
  128. package/dist/scales/ActivityTable.d.ts +103 -0
  129. package/dist/scales/ActivityTable.js +220 -0
  130. package/dist/scales/BandScaleState.svelte.d.ts +41 -15
  131. package/dist/scales/BandScaleState.svelte.js +78 -36
  132. package/dist/scales/ChartScales.svelte.d.ts +2 -2
  133. package/dist/scales/ColorScaleState.svelte.d.ts +36 -8
  134. package/dist/scales/ColorScaleState.svelte.js +118 -24
  135. package/dist/scales/DomainResolver.d.ts +2 -0
  136. package/dist/scales/DomainResolver.js +15 -8
  137. package/dist/scales/ScaleRegistry.svelte.d.ts +9 -0
  138. package/dist/scales/ScaleRegistry.svelte.js +22 -1
  139. package/dist/scales/TimeScaleState.svelte.js +7 -1
  140. package/dist/spec/index.js +1 -0
  141. package/dist/spec/types.d.ts +4 -0
  142. package/dist/theme.css +5 -4
  143. package/dist/transforms/TransformCompiler.d.ts +37 -2
  144. package/dist/transforms/TransformCompiler.js +44 -1
  145. package/dist/transforms/index.d.ts +27 -0
  146. package/dist/transforms/index.js +127 -19
  147. package/dist/types.d.ts +71 -0
  148. 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 layout, 48 bytes)
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 = 1.0;
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(48);
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])[]): void;
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
- size: 48, // Must fit the largest uniform struct (e.g. Rects MarkConstants = 48 bytes)
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(32);
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(32);
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 layout, 48 bytes)
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
- _pad1: u32
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
- if (colorMode === 1 && !colorValueData) return;
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(48);
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 = 16;
11
- const UNIFORM_SIZE = 176; // bytes, must match WGSL struct layout
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 = MAX_AXES * 16;
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
- _pad0: u32,
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
- // u32[6], u32[7] = padding
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. */