@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.
- 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 +30 -6
- 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 +49 -7
- package/dist/components/rendering/Lines.svelte.d.ts +9 -0
- package/dist/components/rendering/ParallelLines.svelte +26 -7
- package/dist/components/rendering/ParallelLines.svelte.d.ts +2 -0
- package/dist/components/rendering/Rects.svelte +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/gpu/ColorScaleShaderLib.d.ts +13 -1
- package/dist/gpu/ColorScaleShaderLib.js +56 -5
- package/dist/gpu/GPUCoordinator.svelte.d.ts +8 -0
- package/dist/gpu/GPUCoordinator.svelte.js +68 -32
- package/dist/gpu/ResolveShader.d.ts +2 -2
- package/dist/gpu/ResolveShader.js +2 -2
- package/dist/gpu/ScaleTemplateSystem.d.ts +19 -0
- package/dist/gpu/ScaleTemplateSystem.js +86 -7
- package/dist/gpu/SizeScaleShaderLib.d.ts +4 -4
- package/dist/gpu/SizeScaleShaderLib.js +4 -4
- package/dist/gpu/identity-slot-packing.d.ts +157 -40
- package/dist/gpu/identity-slot-packing.js +262 -62
- package/dist/gpu/tracked-scales.d.ts +14 -0
- package/dist/gpu/tracked-scales.js +18 -0
- package/dist/index.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
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { TransitionClock, type ClockLoop } from '../animation';
|
|
2
|
+
/**
|
|
3
|
+
* Stiffness for a selection fade: ~0.25 s to settle, against ~0.32 s for a data
|
|
4
|
+
* transition (TRANSITION_STIFFNESS, 240). A click should feel immediate; the
|
|
5
|
+
* charts it filters take their own time to re-query and morph.
|
|
6
|
+
*/
|
|
7
|
+
export declare const SELECTION_STIFFNESS = 400;
|
|
8
|
+
/**
|
|
9
|
+
* How visible an inactive category is, by default: marks are drawn at this
|
|
10
|
+
* opacity (the shaders' SELECTION_DIM_STRENGTH is 1 - this), and
|
|
11
|
+
* band labels fade to it.
|
|
12
|
+
*/
|
|
13
|
+
export declare const DEFAULT_INACTIVE_OPACITY = 0.25;
|
|
14
|
+
/**
|
|
15
|
+
* How visible a category is for its activity: `inactiveOpacity` when
|
|
16
|
+
* unselected, 1 when selected, and in between mid-fade.
|
|
17
|
+
*
|
|
18
|
+
* The CPU twin of the shaders' `selectionDim`, which computes the same thing as
|
|
19
|
+
* `1 - dimStrength * (1 - activity)` with `dimStrength = 1 - inactiveOpacity`.
|
|
20
|
+
* Band labels and legend entries use this, so they match the marks.
|
|
21
|
+
*/
|
|
22
|
+
export declare function selectionOpacity(activity: number, inactiveOpacity?: number): number;
|
|
23
|
+
/**
|
|
24
|
+
* Which categories of a scale are selected, and how far each one has faded.
|
|
25
|
+
*
|
|
26
|
+
* One float per identity id: 1 active, 0 inactive. A categorical scale owns one
|
|
27
|
+
* of these beside its slots, and the shaders read it through the same id they
|
|
28
|
+
* already resolve to a slot, so every mark drawn against the scale dims its
|
|
29
|
+
* inactive categories without knowing whether it is the source of the
|
|
30
|
+
* selection, filtered by it, or outside the filter group.
|
|
31
|
+
*
|
|
32
|
+
* The same shape as SlotTransition, on its own clock: `from` and `to` are the
|
|
33
|
+
* two ends of the fade, the value NOW is `mix(from, to, t)`, the GPU computes it
|
|
34
|
+
* for every id, and `activityOf` for the few a CPU consumer asks about. A
|
|
35
|
+
* SEPARATE clock from the slots', because a click changes what is selected
|
|
36
|
+
* without changing any data, and a data update has nothing to do with dimming.
|
|
37
|
+
* One `t` for every id, not a spring per category: everything fading because of
|
|
38
|
+
* one click arrives together, and there is one moment of rest.
|
|
39
|
+
*/
|
|
40
|
+
export declare class ActivityTable {
|
|
41
|
+
#private;
|
|
42
|
+
/**
|
|
43
|
+
* Bumped whenever `from` or `to` change: a selection, a resize, a settle. The
|
|
44
|
+
* packer compares this rather than the contents, so a frame where only `t`
|
|
45
|
+
* moved uploads nothing.
|
|
46
|
+
*/
|
|
47
|
+
version: number;
|
|
48
|
+
readonly clock: TransitionClock;
|
|
49
|
+
/**
|
|
50
|
+
* `charts` is required: the loop redraws only the charts a clock names, so a
|
|
51
|
+
* fade whose clock named none would animate without anything redrawing it.
|
|
52
|
+
* `loop` is null in unit tests and before a coordinator exists, and then a
|
|
53
|
+
* fade lands at once. `onFrame` is called for every driven frame and once
|
|
54
|
+
* more at rest, for CPU consumers such as axis labels.
|
|
55
|
+
*/
|
|
56
|
+
constructor(loop: ClockLoop | null, options: {
|
|
57
|
+
charts: () => string[];
|
|
58
|
+
onFrame?: () => void;
|
|
59
|
+
stiffness?: number;
|
|
60
|
+
});
|
|
61
|
+
/** How many ids the table covers. */
|
|
62
|
+
get count(): number;
|
|
63
|
+
get from(): Float32Array;
|
|
64
|
+
get to(): Float32Array;
|
|
65
|
+
/** Extrapolated for the frame being drawn. 1 when settled. */
|
|
66
|
+
get t(): number;
|
|
67
|
+
/** True while the clock is in the loop. */
|
|
68
|
+
get isAnimating(): boolean;
|
|
69
|
+
/**
|
|
70
|
+
* How active one id is now, 0..1. An id outside the table is fully active,
|
|
71
|
+
* the same answer the shader gives for an id past the block's count.
|
|
72
|
+
*/
|
|
73
|
+
activityOf(id: number): number;
|
|
74
|
+
/**
|
|
75
|
+
* Cover `count` ids. Existing ids keep their fade; new ones snap to their
|
|
76
|
+
* state rather than fading in.
|
|
77
|
+
*
|
|
78
|
+
* `active` is the selection the new ids join, as ids: null for none, so
|
|
79
|
+
* every new id is active. Without it, a new id is inactive while something
|
|
80
|
+
* is selected, since a category that did not exist when the selection was
|
|
81
|
+
* made is not part of it. Passing it matters when the selection was made
|
|
82
|
+
* before the ids existed (before identity landed, or across a reload): the
|
|
83
|
+
* table has not seen it, and a new id it names must start selected rather
|
|
84
|
+
* than fade in from 0, and one it does not name must start dim rather than
|
|
85
|
+
* fade down from 1.
|
|
86
|
+
*
|
|
87
|
+
* `resize(0)` drops every id, for ids that stopped meaning their category.
|
|
88
|
+
*/
|
|
89
|
+
resize(count: number, active?: readonly number[] | null): void;
|
|
90
|
+
/**
|
|
91
|
+
* Select exactly these ids; every other id fades out. Ids outside the table
|
|
92
|
+
* are ignored. Does nothing when the selection is the one already headed to.
|
|
93
|
+
*/
|
|
94
|
+
setActive(ids: Iterable<number>): void;
|
|
95
|
+
/** Nothing selected: every id fades back to active. */
|
|
96
|
+
clear(): void;
|
|
97
|
+
/** Leave the loop, for an owner being torn down. Later changes land at once. */
|
|
98
|
+
destroy(): void;
|
|
99
|
+
}
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
import { TransitionClock } from '../animation';
|
|
2
|
+
/**
|
|
3
|
+
* Stiffness for a selection fade: ~0.25 s to settle, against ~0.32 s for a data
|
|
4
|
+
* transition (TRANSITION_STIFFNESS, 240). A click should feel immediate; the
|
|
5
|
+
* charts it filters take their own time to re-query and morph.
|
|
6
|
+
*/
|
|
7
|
+
export const SELECTION_STIFFNESS = 400;
|
|
8
|
+
/**
|
|
9
|
+
* How visible an inactive category is, by default: marks are drawn at this
|
|
10
|
+
* opacity (the shaders' SELECTION_DIM_STRENGTH is 1 - this), and
|
|
11
|
+
* band labels fade to it.
|
|
12
|
+
*/
|
|
13
|
+
export const DEFAULT_INACTIVE_OPACITY = 0.25;
|
|
14
|
+
/**
|
|
15
|
+
* How visible a category is for its activity: `inactiveOpacity` when
|
|
16
|
+
* unselected, 1 when selected, and in between mid-fade.
|
|
17
|
+
*
|
|
18
|
+
* The CPU twin of the shaders' `selectionDim`, which computes the same thing as
|
|
19
|
+
* `1 - dimStrength * (1 - activity)` with `dimStrength = 1 - inactiveOpacity`.
|
|
20
|
+
* Band labels and legend entries use this, so they match the marks.
|
|
21
|
+
*/
|
|
22
|
+
export function selectionOpacity(activity, inactiveOpacity = DEFAULT_INACTIVE_OPACITY) {
|
|
23
|
+
return inactiveOpacity + (1 - inactiveOpacity) * activity;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Which categories of a scale are selected, and how far each one has faded.
|
|
27
|
+
*
|
|
28
|
+
* One float per identity id: 1 active, 0 inactive. A categorical scale owns one
|
|
29
|
+
* of these beside its slots, and the shaders read it through the same id they
|
|
30
|
+
* already resolve to a slot, so every mark drawn against the scale dims its
|
|
31
|
+
* inactive categories without knowing whether it is the source of the
|
|
32
|
+
* selection, filtered by it, or outside the filter group.
|
|
33
|
+
*
|
|
34
|
+
* The same shape as SlotTransition, on its own clock: `from` and `to` are the
|
|
35
|
+
* two ends of the fade, the value NOW is `mix(from, to, t)`, the GPU computes it
|
|
36
|
+
* for every id, and `activityOf` for the few a CPU consumer asks about. A
|
|
37
|
+
* SEPARATE clock from the slots', because a click changes what is selected
|
|
38
|
+
* without changing any data, and a data update has nothing to do with dimming.
|
|
39
|
+
* One `t` for every id, not a spring per category: everything fading because of
|
|
40
|
+
* one click arrives together, and there is one moment of rest.
|
|
41
|
+
*/
|
|
42
|
+
export class ActivityTable {
|
|
43
|
+
#from = new Float32Array(0);
|
|
44
|
+
#to = new Float32Array(0);
|
|
45
|
+
/**
|
|
46
|
+
* Where a new selection is computed before it is compared with `to`. Reused,
|
|
47
|
+
* because `setActive` is called on every filter update, and most of those
|
|
48
|
+
* (every brush move on some other field) change nothing here.
|
|
49
|
+
*/
|
|
50
|
+
#scratch = new Float32Array(0);
|
|
51
|
+
/** Whether some categories are selected, which decides what a new id starts as. */
|
|
52
|
+
#selecting = false;
|
|
53
|
+
/**
|
|
54
|
+
* Bumped whenever `from` or `to` change: a selection, a resize, a settle. The
|
|
55
|
+
* packer compares this rather than the contents, so a frame where only `t`
|
|
56
|
+
* moved uploads nothing.
|
|
57
|
+
*/
|
|
58
|
+
version = 0;
|
|
59
|
+
clock;
|
|
60
|
+
#onFrame;
|
|
61
|
+
/**
|
|
62
|
+
* `charts` is required: the loop redraws only the charts a clock names, so a
|
|
63
|
+
* fade whose clock named none would animate without anything redrawing it.
|
|
64
|
+
* `loop` is null in unit tests and before a coordinator exists, and then a
|
|
65
|
+
* fade lands at once. `onFrame` is called for every driven frame and once
|
|
66
|
+
* more at rest, for CPU consumers such as axis labels.
|
|
67
|
+
*/
|
|
68
|
+
constructor(loop, options) {
|
|
69
|
+
this.#onFrame = options.onFrame ?? null;
|
|
70
|
+
this.clock = new TransitionClock({
|
|
71
|
+
loop,
|
|
72
|
+
charts: options.charts,
|
|
73
|
+
stiffness: options.stiffness ?? SELECTION_STIFFNESS,
|
|
74
|
+
onRest: () => this.#onClockRest(),
|
|
75
|
+
onFrame: this.#onFrame ?? undefined
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
/** How many ids the table covers. */
|
|
79
|
+
get count() {
|
|
80
|
+
return this.#to.length;
|
|
81
|
+
}
|
|
82
|
+
get from() {
|
|
83
|
+
return this.#from;
|
|
84
|
+
}
|
|
85
|
+
get to() {
|
|
86
|
+
return this.#to;
|
|
87
|
+
}
|
|
88
|
+
/** Extrapolated for the frame being drawn. 1 when settled. */
|
|
89
|
+
get t() {
|
|
90
|
+
return this.clock.t;
|
|
91
|
+
}
|
|
92
|
+
/** True while the clock is in the loop. */
|
|
93
|
+
get isAnimating() {
|
|
94
|
+
return this.clock.running;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* How active one id is now, 0..1. An id outside the table is fully active,
|
|
98
|
+
* the same answer the shader gives for an id past the block's count.
|
|
99
|
+
*/
|
|
100
|
+
activityOf(id) {
|
|
101
|
+
const f = this.#from[id];
|
|
102
|
+
if (f === undefined)
|
|
103
|
+
return 1;
|
|
104
|
+
return f + (this.#to[id] - f) * this.clock.t;
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Cover `count` ids. Existing ids keep their fade; new ones snap to their
|
|
108
|
+
* state rather than fading in.
|
|
109
|
+
*
|
|
110
|
+
* `active` is the selection the new ids join, as ids: null for none, so
|
|
111
|
+
* every new id is active. Without it, a new id is inactive while something
|
|
112
|
+
* is selected, since a category that did not exist when the selection was
|
|
113
|
+
* made is not part of it. Passing it matters when the selection was made
|
|
114
|
+
* before the ids existed (before identity landed, or across a reload): the
|
|
115
|
+
* table has not seen it, and a new id it names must start selected rather
|
|
116
|
+
* than fade in from 0, and one it does not name must start dim rather than
|
|
117
|
+
* fade down from 1.
|
|
118
|
+
*
|
|
119
|
+
* `resize(0)` drops every id, for ids that stopped meaning their category.
|
|
120
|
+
*/
|
|
121
|
+
resize(count, active) {
|
|
122
|
+
if (count === this.#to.length)
|
|
123
|
+
return;
|
|
124
|
+
const keep = Math.min(this.#to.length, count);
|
|
125
|
+
const selecting = active === undefined ? this.#selecting : active !== null;
|
|
126
|
+
const fill = selecting ? 0 : 1;
|
|
127
|
+
const grown = (old) => {
|
|
128
|
+
const next = new Float32Array(count);
|
|
129
|
+
next.fill(fill);
|
|
130
|
+
next.set(old.subarray(0, keep), 0);
|
|
131
|
+
if (active) {
|
|
132
|
+
for (const id of active)
|
|
133
|
+
if (id >= keep && id < count)
|
|
134
|
+
next[id] = 1;
|
|
135
|
+
}
|
|
136
|
+
return next;
|
|
137
|
+
};
|
|
138
|
+
this.#from = grown(this.#from);
|
|
139
|
+
this.#to = grown(this.#to);
|
|
140
|
+
this.#scratch = new Float32Array(count);
|
|
141
|
+
this.version++;
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Select exactly these ids; every other id fades out. Ids outside the table
|
|
145
|
+
* are ignored. Does nothing when the selection is the one already headed to.
|
|
146
|
+
*/
|
|
147
|
+
setActive(ids) {
|
|
148
|
+
const next = this.#scratch;
|
|
149
|
+
next.fill(0);
|
|
150
|
+
for (const id of ids) {
|
|
151
|
+
if (id >= 0 && id < next.length)
|
|
152
|
+
next[id] = 1;
|
|
153
|
+
}
|
|
154
|
+
this.#selecting = true;
|
|
155
|
+
this.#retarget(next);
|
|
156
|
+
}
|
|
157
|
+
/** Nothing selected: every id fades back to active. */
|
|
158
|
+
clear() {
|
|
159
|
+
const next = this.#scratch;
|
|
160
|
+
next.fill(1);
|
|
161
|
+
this.#selecting = false;
|
|
162
|
+
this.#retarget(next);
|
|
163
|
+
}
|
|
164
|
+
/** Leave the loop, for an owner being torn down. Later changes land at once. */
|
|
165
|
+
destroy() {
|
|
166
|
+
this.clock.stop();
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Fold, point every id at its new state, and wake the loop.
|
|
170
|
+
*
|
|
171
|
+
* Returns early when `next` equals `to`: the fade already in flight (or the
|
|
172
|
+
* rest already reached) is headed exactly there, and restarting it would
|
|
173
|
+
* re-fold and bump the version for nothing, on every filter update.
|
|
174
|
+
*/
|
|
175
|
+
#retarget(next) {
|
|
176
|
+
const from = this.#from;
|
|
177
|
+
const to = this.#to;
|
|
178
|
+
let changed = false;
|
|
179
|
+
for (let id = 0; id < to.length; id++) {
|
|
180
|
+
if (to[id] !== next[id]) {
|
|
181
|
+
changed = true;
|
|
182
|
+
break;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
if (!changed)
|
|
186
|
+
return;
|
|
187
|
+
// THE FOLD: every id continues from where it is now, so a click landing
|
|
188
|
+
// mid-fade reverses or redirects it in place instead of jumping.
|
|
189
|
+
const t = this.clock.t;
|
|
190
|
+
for (let id = 0; id < to.length; id++) {
|
|
191
|
+
from[id] += (to[id] - from[id]) * t;
|
|
192
|
+
to[id] = next[id];
|
|
193
|
+
}
|
|
194
|
+
this.clock.restart();
|
|
195
|
+
this.version++;
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Land exactly on `to`. A spring rests at an epsilon, and a table left at
|
|
199
|
+
* 0.0003 would start its next fold from a value that was never a state.
|
|
200
|
+
*/
|
|
201
|
+
#settle() {
|
|
202
|
+
this.clock.settle();
|
|
203
|
+
this.#from.set(this.#to);
|
|
204
|
+
}
|
|
205
|
+
#onClockRest() {
|
|
206
|
+
this.#settle();
|
|
207
|
+
this.version++;
|
|
208
|
+
// The last frame, at exactly t = 1, which the loop's final frame was not.
|
|
209
|
+
this.#onFrame?.();
|
|
210
|
+
}
|
|
211
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { ScaleBand } from 'd3-scale';
|
|
2
|
-
import type { DimValue, FieldIdentity } from '../query-engine/categorical-identity';
|
|
2
|
+
import type { DimValue, FieldIdentity, SyntheticId } from '../query-engine/categorical-identity';
|
|
3
3
|
import type { ClockLoop } from '../animation';
|
|
4
4
|
import { type BandSlots } from './SlotTransition';
|
|
5
5
|
/**
|
|
@@ -77,10 +77,6 @@ export declare class BandScaleState {
|
|
|
77
77
|
currentDataPoint: number | null;
|
|
78
78
|
constructor(name: string, domain?: DimValue[], config?: BandScaleConfig);
|
|
79
79
|
setReady(): void;
|
|
80
|
-
get activeValues(): Set<DimValue> | null;
|
|
81
|
-
setActiveValues(values: Iterable<DimValue>): void;
|
|
82
|
-
clearActiveValues(): void;
|
|
83
|
-
isActive(value: DimValue): boolean;
|
|
84
80
|
/**
|
|
85
81
|
* Marks the scale dead. Clears nothing, deliberately.
|
|
86
82
|
*
|
|
@@ -121,6 +117,12 @@ export declare class BandScaleState {
|
|
|
121
117
|
* where name is "__facet__species" but sourceField is "species").
|
|
122
118
|
*/
|
|
123
119
|
get sourceField(): string;
|
|
120
|
+
/**
|
|
121
|
+
* The identity key of the dim this scale adopted, or null before it has one.
|
|
122
|
+
* Selection activity is kept per identity key, so this is what the packer
|
|
123
|
+
* points the scale's block at.
|
|
124
|
+
*/
|
|
125
|
+
get identityKey(): string | null;
|
|
124
126
|
/**
|
|
125
127
|
* Enable or disable scale updates
|
|
126
128
|
* When disabled, domain is frozen at current state
|
|
@@ -209,6 +211,24 @@ export declare class BandScaleState {
|
|
|
209
211
|
* undisplayed Other is ignored.
|
|
210
212
|
*/
|
|
211
213
|
setOtherId(id: number | null): void;
|
|
214
|
+
/**
|
|
215
|
+
* This axis's Other bucket, shared by every mark on it that has one.
|
|
216
|
+
*
|
|
217
|
+
* One id per band scale, not per mark or per chart: marks share a band scale
|
|
218
|
+
* only when they agree on what Other is — the scale's key carries the table
|
|
219
|
+
* and the category-reducing transform (its `transformKey`), N included — so
|
|
220
|
+
* everything drawn on one axis lines up in one Other band. A bar and its
|
|
221
|
+
* value labels, or two charts with the identical topN, share it.
|
|
222
|
+
*
|
|
223
|
+
* Reference-counted by mark id. The first holder acquires; holders arriving
|
|
224
|
+
* while that is in flight share it; the last to release gives it back.
|
|
225
|
+
*/
|
|
226
|
+
retainOther(holder: string, acquire: () => Promise<SyntheticId>): Promise<SyntheticId>;
|
|
227
|
+
/**
|
|
228
|
+
* Drop `holder`'s claim on the Other bucket. The last one out hands the id to
|
|
229
|
+
* `release` — once the acquire has landed, if it is still in flight.
|
|
230
|
+
*/
|
|
231
|
+
releaseOther(holder: string, release: (other: SyntheticId) => void): void;
|
|
212
232
|
/**
|
|
213
233
|
* Identity id currently displayed at `slot`, or null when the slot holds no
|
|
214
234
|
* category. Use this to compare a display position — what invertScreenToData
|
|
@@ -375,6 +395,9 @@ export declare class BandScaleState {
|
|
|
375
395
|
* so it fades out over the half slot before that centre reaches the axis end
|
|
376
396
|
* (the animated count), and is gone from there; the same at the start. An
|
|
377
397
|
* entrant's label appears as its band crosses in.
|
|
398
|
+
*
|
|
399
|
+
* A selection's dimming is not here: it belongs to the filter groups of the
|
|
400
|
+
* marks the axis labels, so the axis multiplies it in (BandAxisTicks).
|
|
378
401
|
*/
|
|
379
402
|
tickOpacity(key: number): number;
|
|
380
403
|
/**
|
|
@@ -393,8 +416,6 @@ export declare class BandScaleState {
|
|
|
393
416
|
* re-derived by such a caller.
|
|
394
417
|
*/
|
|
395
418
|
slotPosition(slot: number, rangeSize: number, axis: 'x' | 'y'): number;
|
|
396
|
-
/** Whether a tick's category is outside the current selection. */
|
|
397
|
-
isTickDimmed(tick: BandTick): boolean;
|
|
398
419
|
/**
|
|
399
420
|
* Centre of a (possibly fractional) slot, measured from the start of the
|
|
400
421
|
* range — the same layout the shader's computeBandLayout uses, so a label
|
|
@@ -47,7 +47,6 @@ export class BandScaleState {
|
|
|
47
47
|
// =========================================================================
|
|
48
48
|
// SELECTION STATE
|
|
49
49
|
// =========================================================================
|
|
50
|
-
#activeValues = $state(null); // null = all active, Set = only these active
|
|
51
50
|
// =========================================================================
|
|
52
51
|
// POINTER STATE
|
|
53
52
|
// =========================================================================
|
|
@@ -104,21 +103,6 @@ export class BandScaleState {
|
|
|
104
103
|
setReady() {
|
|
105
104
|
this.ready = true;
|
|
106
105
|
}
|
|
107
|
-
// =========================================================================
|
|
108
|
-
// ACTIVE VALUES (selection feedback)
|
|
109
|
-
// =========================================================================
|
|
110
|
-
get activeValues() {
|
|
111
|
-
return this.#activeValues;
|
|
112
|
-
}
|
|
113
|
-
setActiveValues(values) {
|
|
114
|
-
this.#activeValues = values instanceof Set ? values : new Set(values);
|
|
115
|
-
}
|
|
116
|
-
clearActiveValues() {
|
|
117
|
-
this.#activeValues = null;
|
|
118
|
-
}
|
|
119
|
-
isActive(value) {
|
|
120
|
-
return this.#activeValues == null || this.#activeValues.has(value);
|
|
121
|
-
}
|
|
122
106
|
/**
|
|
123
107
|
* Marks the scale dead. Clears nothing, deliberately.
|
|
124
108
|
*
|
|
@@ -186,6 +170,15 @@ export class BandScaleState {
|
|
|
186
170
|
get sourceField() {
|
|
187
171
|
return this.#sourceField ?? this.name;
|
|
188
172
|
}
|
|
173
|
+
/**
|
|
174
|
+
* The identity key of the dim this scale adopted, or null before it has one.
|
|
175
|
+
* Selection activity is kept per identity key, so this is what the packer
|
|
176
|
+
* points the scale's block at.
|
|
177
|
+
*/
|
|
178
|
+
get identityKey() {
|
|
179
|
+
// Only while the channel carries ids, as for colour.
|
|
180
|
+
return this.hasIdentity ? this.#resolver.identity.key || null : null;
|
|
181
|
+
}
|
|
189
182
|
// =========================================================================
|
|
190
183
|
// FREEZE/DEBOUNCE CONTROL
|
|
191
184
|
// =========================================================================
|
|
@@ -334,6 +327,49 @@ export class BandScaleState {
|
|
|
334
327
|
setOtherId(id) {
|
|
335
328
|
this.#slots.otherId = id;
|
|
336
329
|
}
|
|
330
|
+
/**
|
|
331
|
+
* This axis's Other bucket, shared by every mark on it that has one.
|
|
332
|
+
*
|
|
333
|
+
* One id per band scale, not per mark or per chart: marks share a band scale
|
|
334
|
+
* only when they agree on what Other is — the scale's key carries the table
|
|
335
|
+
* and the category-reducing transform (its `transformKey`), N included — so
|
|
336
|
+
* everything drawn on one axis lines up in one Other band. A bar and its
|
|
337
|
+
* value labels, or two charts with the identical topN, share it.
|
|
338
|
+
*
|
|
339
|
+
* Reference-counted by mark id. The first holder acquires; holders arriving
|
|
340
|
+
* while that is in flight share it; the last to release gives it back.
|
|
341
|
+
*/
|
|
342
|
+
retainOther(holder, acquire) {
|
|
343
|
+
this.#otherHolders.add(holder);
|
|
344
|
+
if (!this.#other) {
|
|
345
|
+
const pending = acquire();
|
|
346
|
+
this.#other = pending;
|
|
347
|
+
pending.then((other) => {
|
|
348
|
+
if (this.#other === pending)
|
|
349
|
+
this.setOtherId(other.id);
|
|
350
|
+
}, () => {
|
|
351
|
+
if (this.#other === pending)
|
|
352
|
+
this.#other = null;
|
|
353
|
+
});
|
|
354
|
+
}
|
|
355
|
+
return this.#other;
|
|
356
|
+
}
|
|
357
|
+
/**
|
|
358
|
+
* Drop `holder`'s claim on the Other bucket. The last one out hands the id to
|
|
359
|
+
* `release` — once the acquire has landed, if it is still in flight.
|
|
360
|
+
*/
|
|
361
|
+
releaseOther(holder, release) {
|
|
362
|
+
if (!this.#otherHolders.delete(holder) || this.#otherHolders.size > 0)
|
|
363
|
+
return;
|
|
364
|
+
const pending = this.#other;
|
|
365
|
+
this.#other = null;
|
|
366
|
+
this.setOtherId(null);
|
|
367
|
+
pending?.then(release, () => { });
|
|
368
|
+
}
|
|
369
|
+
/** The marks holding this axis's Other bucket. */
|
|
370
|
+
#otherHolders = new Set();
|
|
371
|
+
/** The bucket they share, while any holds it. */
|
|
372
|
+
#other = null;
|
|
337
373
|
// -------------------------------------------------------------------------
|
|
338
374
|
// IDENTITY <-> DISPLAY, FOR CPU CONSUMERS
|
|
339
375
|
// -------------------------------------------------------------------------
|
|
@@ -709,6 +745,9 @@ export class BandScaleState {
|
|
|
709
745
|
* so it fades out over the half slot before that centre reaches the axis end
|
|
710
746
|
* (the animated count), and is gone from there; the same at the start. An
|
|
711
747
|
* entrant's label appears as its band crosses in.
|
|
748
|
+
*
|
|
749
|
+
* A selection's dimming is not here: it belongs to the filter groups of the
|
|
750
|
+
* marks the axis labels, so the axis multiplies it in (BandAxisTicks).
|
|
712
751
|
*/
|
|
713
752
|
tickOpacity(key) {
|
|
714
753
|
void this.#slotFrame;
|
|
@@ -738,10 +777,6 @@ export class BandScaleState {
|
|
|
738
777
|
const centre = this.slotCenter(slot, rangeSize);
|
|
739
778
|
return axis === 'y' ? rangeSize - centre : centre;
|
|
740
779
|
}
|
|
741
|
-
/** Whether a tick's category is outside the current selection. */
|
|
742
|
-
isTickDimmed(tick) {
|
|
743
|
-
return tick.value != null && !this.isActive(tick.value);
|
|
744
|
-
}
|
|
745
780
|
/**
|
|
746
781
|
* Centre of a (possibly fractional) slot, measured from the start of the
|
|
747
782
|
* range — the same layout the shader's computeBandLayout uses, so a label
|
|
@@ -65,8 +65,8 @@ export declare class ChartScales {
|
|
|
65
65
|
};
|
|
66
66
|
get xScaleState(): ScaleStateInstance | null;
|
|
67
67
|
get yScaleState(): ScaleStateInstance | null;
|
|
68
|
-
baseXScale: import("d3-scale").
|
|
69
|
-
baseYScale: import("d3-scale").
|
|
68
|
+
baseXScale: import("d3-scale").ScaleBand<import("../query-engine/categorical-identity").DimValue> | import("d3-scale").ScaleContinuousNumeric<number, number, never>;
|
|
69
|
+
baseYScale: import("d3-scale").ScaleBand<import("../query-engine/categorical-identity").DimValue> | import("d3-scale").ScaleContinuousNumeric<number, number, never>;
|
|
70
70
|
get xScale(): D3Scale | null;
|
|
71
71
|
get yScale(): D3Scale | null;
|
|
72
72
|
get xTargetScale(): D3Scale | null;
|
|
@@ -25,7 +25,21 @@ export declare class ColorScaleState {
|
|
|
25
25
|
domain: (string | number)[];
|
|
26
26
|
ready: boolean;
|
|
27
27
|
_version: number;
|
|
28
|
+
/**
|
|
29
|
+
* Read by the packer every frame. The rank table covers the ids the field had
|
|
30
|
+
* when it was built, and the field's identity grows in place: another table
|
|
31
|
+
* appends categories, and a chart acquiring an Other bucket appends its id,
|
|
32
|
+
* any chart, since this scale is shared per field. An id past the table's end
|
|
33
|
+
* reads as unranked and draws transparent, so a grown identity rebuilds it
|
|
34
|
+
* here, once per growth.
|
|
35
|
+
*/
|
|
28
36
|
get slots(): SlotSource;
|
|
37
|
+
/**
|
|
38
|
+
* Reactive: changes whenever the palette ranks are rebuilt, which is also when
|
|
39
|
+
* identity is adopted. A reader of `identityKey` or `getColor` in a derived
|
|
40
|
+
* reads this to re-run then.
|
|
41
|
+
*/
|
|
42
|
+
get ranksRevision(): number;
|
|
29
43
|
/**
|
|
30
44
|
* Called by the packer when this scale's block moves, and told its own base
|
|
31
45
|
* rather than having it written into its config from outside.
|
|
@@ -51,9 +65,14 @@ export declare class ColorScaleState {
|
|
|
51
65
|
setSlotBase(base: number): void;
|
|
52
66
|
constructor(config: ColorScaleConfig, registry: ScaleRegistry, gpuCoordinator: GPUCoordinator | null);
|
|
53
67
|
get field(): string;
|
|
68
|
+
/**
|
|
69
|
+
* The identity key of the dim this scale adopted, or null before it has one.
|
|
70
|
+
* Selection activity is kept per identity key, so this is what the packer
|
|
71
|
+
* points the scale's block at.
|
|
72
|
+
*/
|
|
73
|
+
get identityKey(): string | null;
|
|
54
74
|
get scheme(): string;
|
|
55
75
|
get scaleType(): ColorScaleType;
|
|
56
|
-
get activeValues(): Set<string | number> | null;
|
|
57
76
|
get isOrdinal(): boolean;
|
|
58
77
|
get isContinuous(): boolean;
|
|
59
78
|
/**
|
|
@@ -73,9 +92,14 @@ export declare class ColorScaleState {
|
|
|
73
92
|
*/
|
|
74
93
|
get d3Scale(): D3ColorScale | null;
|
|
75
94
|
/**
|
|
76
|
-
*
|
|
95
|
+
* The id the coordinator tracks this scale by, as it does every scale.
|
|
96
|
+
*
|
|
97
|
+
* Tracking is what puts the scale in front of the identity slot packer
|
|
98
|
+
* (see tracked-scales.ts). Without an id, its rank table and selection
|
|
99
|
+
* activity were never packed: the shader read the channel's dim id as a
|
|
100
|
+
* palette index, and selection dimming had nothing to read.
|
|
77
101
|
*/
|
|
78
|
-
get
|
|
102
|
+
get id(): string;
|
|
79
103
|
/**
|
|
80
104
|
* Converts raw data into GPU-ready values.
|
|
81
105
|
* - Ordinal: string -> 0-based index (-1 = null/unknown, 0+ = categories)
|
|
@@ -95,13 +119,15 @@ export declare class ColorScaleState {
|
|
|
95
119
|
*/
|
|
96
120
|
setIdentity(identity: FieldIdentity): void;
|
|
97
121
|
/** True once this scale's channel carries dim ids rather than palette indices. */
|
|
122
|
+
/** The field's live id <-> value bijection, once adopted. */
|
|
123
|
+
get identity(): FieldIdentity | null;
|
|
98
124
|
get hasIdentity(): boolean;
|
|
99
125
|
/**
|
|
100
126
|
* Source value for a channel id — the decode side of the encode the query
|
|
101
127
|
* performs, for tooltips and legends. Undefined without identity, where the
|
|
102
128
|
* channel carries a palette index and the caller should read the domain.
|
|
103
129
|
*/
|
|
104
|
-
categoryAt(id: number): string | number | undefined;
|
|
130
|
+
categoryAt(id: number): string | number | null | undefined;
|
|
105
131
|
/** Update the domain. Triggers GPU re-upload. */
|
|
106
132
|
setDomain(newDomain: (string | number)[]): void;
|
|
107
133
|
/**
|
|
@@ -139,14 +165,16 @@ export declare class ColorScaleState {
|
|
|
139
165
|
* Returns NULL_COLOR for values that are unmappable under the current
|
|
140
166
|
* transform (e.g. non-positive values under log), mirroring the GPU shader.
|
|
141
167
|
*/
|
|
142
|
-
getColor(value: string | number): string;
|
|
168
|
+
getColor(value: string | number | null): string;
|
|
143
169
|
/**
|
|
144
170
|
* Get color as RGBA array (0-1 range).
|
|
145
171
|
*/
|
|
146
172
|
getColorRGBA(value: string | number): RGBA;
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
173
|
+
/**
|
|
174
|
+
* Charts drawing this scale. Asked of the registry, as BandScaleState does,
|
|
175
|
+
* so it is current when charts come and go.
|
|
176
|
+
*/
|
|
177
|
+
get _affectedChartsArray(): string[];
|
|
150
178
|
/**
|
|
151
179
|
* Marks the scale dead. Clears nothing, deliberately — see
|
|
152
180
|
* BandScaleState.destroy for the full argument.
|