@fjader/svelte 0.2.2 → 0.2.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -0
- package/dist/components/axes/AxisBottom.svelte +23 -6
- package/dist/components/axes/AxisBottom.svelte.d.ts +10 -0
- package/dist/components/axes/AxisLeft.svelte +77 -8
- package/dist/components/axes/AxisLeft.svelte.d.ts +17 -0
- package/dist/components/axes/BandAxisTicks.svelte +64 -25
- package/dist/components/axes/BandAxisTicks.svelte.d.ts +5 -5
- package/dist/components/axes/axis-title.d.ts +30 -0
- package/dist/components/axes/axis-title.js +77 -0
- package/dist/components/charts/ChartCell.svelte +1 -1
- package/dist/components/charts/ChartContainer.svelte +12 -3
- package/dist/components/charts/ChartContainer.svelte.d.ts +7 -0
- package/dist/components/guides/legend/ContinuousColorLegend.svelte +9 -2
- package/dist/components/guides/legend/OrdinalColorLegend.svelte +96 -35
- package/dist/components/marks/AreaMarkX.svelte +21 -6
- package/dist/components/marks/AreaMarkY.svelte +21 -6
- package/dist/components/marks/BarMarkX.svelte +16 -1
- package/dist/components/marks/BarMarkY.svelte +16 -1
- package/dist/components/marks/DotsMark.svelte +5 -0
- package/dist/components/marks/LineMark.svelte +8 -1
- package/dist/components/marks/ParallelCoordinatesMark.svelte +10 -0
- package/dist/components/marks/ParallelCoordinatesMark.svelte.d.ts +5 -0
- package/dist/components/marks/RectMark.svelte +5 -0
- package/dist/components/marks/RectXMark.svelte +5 -0
- package/dist/components/marks/RectYMark.svelte +5 -0
- package/dist/components/marks/RuleMarkX.svelte +12 -2
- package/dist/components/marks/RuleMarkY.svelte +12 -2
- package/dist/components/marks/TextMark.svelte +76 -13
- package/dist/components/marks/lifecycle/MarkLifecycleV2.svelte.d.ts +21 -2
- package/dist/components/marks/lifecycle/MarkLifecycleV2.svelte.js +300 -42
- package/dist/components/marks/lifecycle/domain-queries.d.ts +3 -1
- package/dist/components/marks/lifecycle/domain-queries.js +22 -15
- package/dist/components/marks/lifecycle/domain-registration.js +2 -6
- package/dist/components/marks/lifecycle/other-rows.svelte.d.ts +104 -0
- package/dist/components/marks/lifecycle/other-rows.svelte.js +193 -0
- package/dist/components/marks/lifecycle/other-split.d.ts +80 -0
- package/dist/components/marks/lifecycle/other-split.js +112 -0
- package/dist/components/marks/lifecycle/query-builder.d.ts +26 -1
- package/dist/components/marks/lifecycle/query-builder.js +25 -5
- package/dist/components/marks/lifecycle/transform-scale-key.d.ts +16 -0
- package/dist/components/marks/lifecycle/transform-scale-key.js +27 -0
- package/dist/components/marks/utils/area-outline.d.ts +24 -0
- package/dist/components/marks/utils/area-outline.js +10 -0
- package/dist/components/marks/utils/reactive-config.d.ts +1 -1
- package/dist/components/marks/utils/reactive-config.js +9 -0
- package/dist/components/marks/utils/tooltip-helpers.d.ts +4 -1
- package/dist/components/marks/utils/tooltip-helpers.js +2 -1
- package/dist/components/rendering/Areas.svelte +30 -6
- package/dist/components/rendering/Areas.svelte.d.ts +2 -0
- package/dist/components/rendering/BaseGPURenderer.svelte.d.ts +3 -1
- package/dist/components/rendering/BaseGPURenderer.svelte.js +8 -3
- package/dist/components/rendering/Dots.svelte +24 -3
- package/dist/components/rendering/Dots.svelte.d.ts +2 -0
- package/dist/components/rendering/GridLine.svelte +2 -1
- package/dist/components/rendering/Lines.svelte +49 -7
- package/dist/components/rendering/Lines.svelte.d.ts +9 -0
- package/dist/components/rendering/ParallelLines.svelte +26 -7
- package/dist/components/rendering/ParallelLines.svelte.d.ts +2 -0
- package/dist/components/rendering/Rects.svelte +129 -20
- package/dist/components/rendering/Rects.svelte.d.ts +9 -0
- package/dist/components/rendering/RuleX.svelte +20 -5
- package/dist/components/rendering/RuleX.svelte.d.ts +2 -0
- package/dist/components/rendering/RuleY.svelte +20 -5
- package/dist/components/rendering/RuleY.svelte.d.ts +2 -0
- package/dist/components/rendering/activity-reads.d.ts +111 -0
- package/dist/components/rendering/activity-reads.js +154 -0
- package/dist/components/rendering/uniform-sizes.d.ts +26 -0
- package/dist/components/rendering/uniform-sizes.js +28 -0
- package/dist/config/ChartConfig.svelte.d.ts +5 -1
- package/dist/gpu/ColorScaleShaderLib.d.ts +13 -1
- package/dist/gpu/ColorScaleShaderLib.js +56 -5
- package/dist/gpu/GPUCoordinator.svelte.d.ts +8 -0
- package/dist/gpu/GPUCoordinator.svelte.js +68 -32
- package/dist/gpu/MorphedChannels.d.ts +6 -32
- package/dist/gpu/MorphedChannels.js +45 -3
- package/dist/gpu/ResolveShader.d.ts +2 -2
- package/dist/gpu/ResolveShader.js +2 -2
- package/dist/gpu/ScaleTemplateSystem.d.ts +19 -0
- package/dist/gpu/ScaleTemplateSystem.js +111 -11
- package/dist/gpu/SizeScaleShaderLib.d.ts +4 -4
- package/dist/gpu/SizeScaleShaderLib.js +4 -4
- package/dist/gpu/identity-slot-packing.d.ts +157 -40
- package/dist/gpu/identity-slot-packing.js +262 -62
- package/dist/gpu/tracked-scales.d.ts +14 -0
- package/dist/gpu/tracked-scales.js +18 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +4 -0
- package/dist/interactors/BandLabelToggle.svelte +5 -9
- package/dist/interactors/BandLabelToggle.svelte.d.ts +3 -3
- package/dist/interactors/DerivedInterval1D.svelte +26 -54
- package/dist/interactors/Interval1D.svelte +8 -2
- package/dist/interactors/Interval2D.svelte +8 -2
- package/dist/interactors/ParallelAxisSource.svelte +14 -5
- package/dist/interactors/ParallelAxisSource.svelte.d.ts +2 -1
- package/dist/interactors/ParallelBrushes.svelte +8 -9
- package/dist/interactors/ParallelBrushes.svelte.d.ts +3 -3
- package/dist/interactors/Toggle.svelte +48 -38
- package/dist/interactors/Toggle.svelte.d.ts +4 -3
- package/dist/interactors/Tooltip.svelte +140 -42
- package/dist/interactors/TooltipManager.svelte.d.ts +20 -5
- package/dist/interactors/TooltipManager.svelte.js +40 -10
- package/dist/interactors/other-tooltip.d.ts +16 -0
- package/dist/interactors/other-tooltip.js +22 -0
- package/dist/interactors/relay-clauses.d.ts +23 -0
- package/dist/interactors/relay-clauses.js +60 -0
- package/dist/query-engine/CrossfilterSelection.svelte.d.ts +88 -15
- package/dist/query-engine/CrossfilterSelection.svelte.js +94 -20
- package/dist/query-engine/FieldActivity.svelte.d.ts +128 -0
- package/dist/query-engine/FieldActivity.svelte.js +378 -0
- package/dist/query-engine/FilterGroup.svelte.d.ts +76 -30
- package/dist/query-engine/FilterGroup.svelte.js +223 -124
- package/dist/query-engine/FilterGroupRegistry.svelte.d.ts +73 -3
- package/dist/query-engine/FilterGroupRegistry.svelte.js +161 -12
- package/dist/query-engine/MarkHandle.svelte.d.ts +9 -1
- package/dist/query-engine/MarkHandle.svelte.js +12 -2
- package/dist/query-engine/QueryEngine.d.ts +52 -16
- package/dist/query-engine/QueryEngine.js +116 -34
- package/dist/query-engine/categorical-identity.d.ts +114 -27
- package/dist/query-engine/categorical-identity.js +225 -51
- package/dist/query-engine/index.d.ts +2 -1
- package/dist/query-engine/index.js +1 -0
- package/dist/query-engine/preagg/Preagg.d.ts +1 -1
- package/dist/query-engine/preagg/Preagg.js +11 -9
- package/dist/query-engine/preagg/plan.d.ts +14 -5
- package/dist/query-engine/preagg/plan.js +44 -18
- package/dist/query-engine/toggle-predicate.d.ts +21 -0
- package/dist/query-engine/toggle-predicate.js +41 -2
- package/dist/scales/ActivityTable.d.ts +103 -0
- package/dist/scales/ActivityTable.js +220 -0
- package/dist/scales/BandScaleState.svelte.d.ts +41 -15
- package/dist/scales/BandScaleState.svelte.js +78 -36
- package/dist/scales/ChartScales.svelte.d.ts +2 -2
- package/dist/scales/ColorScaleState.svelte.d.ts +36 -8
- package/dist/scales/ColorScaleState.svelte.js +118 -24
- package/dist/scales/DomainResolver.d.ts +2 -0
- package/dist/scales/DomainResolver.js +15 -8
- package/dist/scales/ScaleRegistry.svelte.d.ts +9 -0
- package/dist/scales/ScaleRegistry.svelte.js +22 -1
- package/dist/scales/TimeScaleState.svelte.js +7 -1
- package/dist/spec/index.js +1 -0
- package/dist/spec/types.d.ts +4 -0
- package/dist/theme.css +5 -4
- package/dist/transforms/TransformCompiler.d.ts +37 -2
- package/dist/transforms/TransformCompiler.js +44 -1
- package/dist/transforms/index.d.ts +27 -0
- package/dist/transforms/index.js +127 -19
- package/dist/types.d.ts +71 -0
- package/package.json +3 -2
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
* License; see NOTICE.
|
|
13
13
|
*/
|
|
14
14
|
import { isAggregateExpression, isNode, collectColumns, column, and, isBetween, literal, Query, sql as sqlTag } from '@uwdata/mosaic-sql';
|
|
15
|
-
import { togglePredicate } from '../toggle-predicate';
|
|
15
|
+
import { BITMAP_MIN_IDS, idBitmapPredicate, togglePredicate } from '../toggle-predicate';
|
|
16
16
|
import { rewriteForPreagg, buildBinExpr, binsFineEnough, BIN_WINDOW_MARGIN } from '../sufficient-statistics';
|
|
17
17
|
import { assembleQuery, castNumeric, castReal } from '../../components/marks/lifecycle/query-builder';
|
|
18
18
|
/**
|
|
@@ -37,6 +37,11 @@ export function planPreagg(descriptor, dims, identity) {
|
|
|
37
37
|
const { parts, table, baseTable, schema } = descriptor;
|
|
38
38
|
if (!parts.hasAggregates && !parts.hasWindowAggregates)
|
|
39
39
|
return null;
|
|
40
|
+
// A having() on a preagg table would count its groups, not the source rows, and
|
|
41
|
+
// return the wrong rows without an error. The built-in transforms add none; see
|
|
42
|
+
// TransformCompiler.hasHaving for why, and for what supporting it would take.
|
|
43
|
+
if (descriptor.transformCompiler.hasHaving())
|
|
44
|
+
return null;
|
|
40
45
|
// Analyze selectDef for preagg compatibility
|
|
41
46
|
const analysis = rewriteForPreagg(parts.selectDef, parts.passthroughChannels);
|
|
42
47
|
if (!analysis)
|
|
@@ -74,7 +79,8 @@ export function planPreagg(descriptor, dims, identity) {
|
|
|
74
79
|
activeMeta.push({ kind: 'value', field, column: channel, byId: true });
|
|
75
80
|
return;
|
|
76
81
|
}
|
|
77
|
-
// One more group for -1:
|
|
82
|
+
// One more group for -1: a value the dim does not hold yet. NULL has an id
|
|
83
|
+
// of its own, counted in the values.
|
|
78
84
|
const idGroups = identity.values(field).length + 1;
|
|
79
85
|
if (identity.dimCovers(field, baseTable)) {
|
|
80
86
|
// Case 2.
|
|
@@ -114,6 +120,7 @@ export function planPreagg(descriptor, dims, identity) {
|
|
|
114
120
|
groupby: [...preaggGroupBy, ...activeGroupBy],
|
|
115
121
|
// Sorted by bin columns for fast range scans
|
|
116
122
|
orderby: binOrder,
|
|
123
|
+
where: descriptor.transformCompiler.rowFilters(),
|
|
117
124
|
joins
|
|
118
125
|
},
|
|
119
126
|
outputSelect,
|
|
@@ -217,16 +224,16 @@ export function estimateGroups(markKeys, activeKeys) {
|
|
|
217
224
|
* mismatch the table's SQL is rebuilt from the current clauses and compared.
|
|
218
225
|
* Once that fails, frames that still see the same clauses skip the rebuild.
|
|
219
226
|
*/
|
|
220
|
-
export function bakedClausesHold(
|
|
227
|
+
export function bakedClausesHold(route, selection, state) {
|
|
221
228
|
const { basis } = state;
|
|
222
229
|
const { activeSourceId } = basis;
|
|
223
|
-
if (selection.bakedClausesMatch(
|
|
230
|
+
if (selection.bakedClausesMatch(route, activeSourceId, basis.bakedClauses))
|
|
224
231
|
return true;
|
|
225
|
-
if (state.mismatch && selection.bakedClausesMatch(
|
|
232
|
+
if (state.mismatch && selection.bakedClausesMatch(route, activeSourceId, state.mismatch)) {
|
|
226
233
|
return false;
|
|
227
234
|
}
|
|
228
|
-
const current = selection.bakedClauses(
|
|
229
|
-
const sql = preaggSelectSQL(state.creation, selection.bakedPredicates(
|
|
235
|
+
const current = selection.bakedClauses(route, activeSourceId);
|
|
236
|
+
const sql = preaggSelectSQL(state.creation, selection.bakedPredicates(route, activeSourceId));
|
|
230
237
|
if (sql === state.sql) {
|
|
231
238
|
basis.bakedClauses = current;
|
|
232
239
|
state.mismatch = null;
|
|
@@ -241,15 +248,16 @@ export function bakedClausesHold(markId, selection, state) {
|
|
|
241
248
|
* Builds modified QueryParts from the stored outputSelect + bin filters,
|
|
242
249
|
* then delegates to assembleQuery().
|
|
243
250
|
*/
|
|
244
|
-
export function preaggQuerySQL(
|
|
251
|
+
export function preaggQuerySQL(route, descriptor, selection, preagg, params, identity) {
|
|
245
252
|
const { activeMeta, outputSelect, windowChannels } = preagg;
|
|
246
253
|
const { parts, transformCompiler } = descriptor;
|
|
247
254
|
// --- The active source's clause as a filter on the active columns. None
|
|
248
|
-
// when it has no clause (an empty toggle: every category) or
|
|
249
|
-
// this mark.
|
|
255
|
+
// when it has no clause (an empty toggle: every category) or does not
|
|
256
|
+
// filter this mark.
|
|
250
257
|
const filters = [];
|
|
251
|
-
const
|
|
252
|
-
|
|
258
|
+
const activeSourceId = preagg.basis.activeSourceId;
|
|
259
|
+
const clause = selection.get(activeSourceId);
|
|
260
|
+
if (clause && selection.applies(activeSourceId, clause, route)) {
|
|
253
261
|
// A drag frame runs this per mark: nothing below allocates for a brush
|
|
254
262
|
// beyond its filter, so the value arrays are created only when needed.
|
|
255
263
|
let binCount = 0;
|
|
@@ -278,8 +286,9 @@ export function preaggQuerySQL(markId, descriptor, selection, preagg, params, id
|
|
|
278
286
|
}
|
|
279
287
|
if (valueExprs && valueMeta) {
|
|
280
288
|
// A toggle's clause holds tuples in its field order. Id columns
|
|
281
|
-
// filter by id; a value with no id (
|
|
282
|
-
// be expressed, so this query goes to the base
|
|
289
|
+
// filter by id (the NULL category has one); a value with no id (not in
|
|
290
|
+
// the dim yet) cannot be expressed, so this query goes to the base
|
|
291
|
+
// table instead.
|
|
283
292
|
const tuples = [];
|
|
284
293
|
for (const tuple of clause.value) {
|
|
285
294
|
const mapped = [];
|
|
@@ -292,7 +301,17 @@ export function preaggQuerySQL(markId, descriptor, selection, preagg, params, id
|
|
|
292
301
|
}
|
|
293
302
|
tuples.push(mapped);
|
|
294
303
|
}
|
|
295
|
-
|
|
304
|
+
// One field by id, and many of them (a click on a large Other): a
|
|
305
|
+
// constant-size bitmap rather than a long IN list, which DuckDB spends
|
|
306
|
+
// most of its time parsing. Measured on a preagg table: 1 ms against
|
|
307
|
+
// 20 ms for IN of 9 000 ids.
|
|
308
|
+
const only = valueMeta.length === 1 ? valueMeta[0] : null;
|
|
309
|
+
if (only?.byId && tuples.length > BITMAP_MIN_IDS) {
|
|
310
|
+
filters.push(idBitmapPredicate(valueExprs[0], tuples.map((t) => t[0]), identity.identity(only.field).values.length));
|
|
311
|
+
}
|
|
312
|
+
else {
|
|
313
|
+
filters.push(togglePredicate(valueExprs, tuples));
|
|
314
|
+
}
|
|
296
315
|
}
|
|
297
316
|
}
|
|
298
317
|
// --- Build selectDef: CAST recombined aggregates; dimensions are already
|
|
@@ -322,13 +341,19 @@ export function preaggQuerySQL(markId, descriptor, selection, preagg, params, id
|
|
|
322
341
|
hasSQLTransforms: parts.hasSQLTransforms,
|
|
323
342
|
orderChannels: parts.orderChannels,
|
|
324
343
|
aliasMap: parts.aliasMap,
|
|
325
|
-
passthroughChannels: parts.passthroughChannels
|
|
344
|
+
passthroughChannels: parts.passthroughChannels,
|
|
345
|
+
// nulls="drop", read through this select: each dropped channel is a group
|
|
346
|
+
// key, so the table keeps NULL as its own group, and dropping that group
|
|
347
|
+
// here removes exactly what the base scan would have left out.
|
|
348
|
+
nullDrops: parts.nullDrops
|
|
326
349
|
};
|
|
327
350
|
return assembleQuery({
|
|
328
351
|
table: preagg.table,
|
|
329
352
|
parts: preaggParts,
|
|
330
353
|
filter: filters,
|
|
331
354
|
transformCompiler,
|
|
355
|
+
// The table was built with them.
|
|
356
|
+
skipRowFilters: true,
|
|
332
357
|
limit: params.limit,
|
|
333
358
|
window: windowChannels ? { channels: windowChannels, cteName: '__preagg_cte' } : null
|
|
334
359
|
}).toString();
|
|
@@ -336,8 +361,9 @@ export function preaggQuerySQL(markId, descriptor, selection, preagg, params, id
|
|
|
336
361
|
/** A preagg table's SELECT. Build and validity check share it, so equal SQL means an equal table. */
|
|
337
362
|
export function preaggSelectSQL(creation, filters) {
|
|
338
363
|
const query = Query.from(creation.table).select(creation.select).groupby(creation.groupby);
|
|
339
|
-
|
|
340
|
-
|
|
364
|
+
const where = [...creation.where, ...filters];
|
|
365
|
+
if (where.length > 0)
|
|
366
|
+
query.where(and(where));
|
|
341
367
|
if (creation.joins.length === 0)
|
|
342
368
|
return query.orderby(creation.orderby).toString();
|
|
343
369
|
// Map fields grouped by value to their ids on the grouped result: a join
|
|
@@ -8,3 +8,24 @@ import { type ExprNode } from '@uwdata/mosaic-sql';
|
|
|
8
8
|
* so a predicate is never rewritten after the fact.
|
|
9
9
|
*/
|
|
10
10
|
export declare function togglePredicate(exprs: ExprNode[], tuples: unknown[][]): ExprNode;
|
|
11
|
+
/**
|
|
12
|
+
* Above this many ids, a selection over an id column is a bitmap, not an `IN`
|
|
13
|
+
* list (see idBitmapPredicate). The crossover measured in DuckDB-WASM 1.30 lies
|
|
14
|
+
* between 500 values (`IN`, 29 ms on 2 M rows) and the bitmap (26 ms).
|
|
15
|
+
*/
|
|
16
|
+
export declare const BITMAP_MIN_IDS = 256;
|
|
17
|
+
/**
|
|
18
|
+
* `expr`'s id is one of `ids`, as one constant-size literal:
|
|
19
|
+
* `get_bit('<bits>'::BIT, <index>) = 1`.
|
|
20
|
+
*
|
|
21
|
+
* Never `NOT IN`, whatever the selection's size: DuckDB evaluates it as a chain
|
|
22
|
+
* of `<>`, 2.8 s on 2 M rows for 1 000 ids. And never a long `IN`, which it
|
|
23
|
+
* spends most of its time parsing.
|
|
24
|
+
*
|
|
25
|
+
* Bit `id + 1` is set for each selected id; bit 0 is id -1 ("not in the dim
|
|
26
|
+
* yet") and is never set; the last bit, `size + 1`, is a sentinel kept 0. The
|
|
27
|
+
* index is clamped onto [0, sentinel], so an id appended to the dim after the
|
|
28
|
+
* selection was made reads the sentinel (it was not selected: a snapshot), and
|
|
29
|
+
* get_bit never sees an index out of range, which is an error rather than a 0.
|
|
30
|
+
*/
|
|
31
|
+
export declare function idBitmapPredicate(expr: ExprNode, ids: readonly number[], size: number): ExprNode;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { and as sqlAnd, isIn, isNotDistinct, literal, or } from '@uwdata/mosaic-sql';
|
|
1
|
+
import { and as sqlAnd, isIn, isNotDistinct, literal, or, sql as sqlTag } from '@uwdata/mosaic-sql';
|
|
2
2
|
/**
|
|
3
3
|
* A toggle's clause over its selected tuples: `IN` for one field, an `OR` of
|
|
4
4
|
* `IS NOT DISTINCT FROM` tuples for several, so a NULL member matches NULL.
|
|
@@ -9,8 +9,47 @@ import { and as sqlAnd, isIn, isNotDistinct, literal, or } from '@uwdata/mosaic-
|
|
|
9
9
|
*/
|
|
10
10
|
export function togglePredicate(exprs, tuples) {
|
|
11
11
|
if (exprs.length === 1) {
|
|
12
|
-
|
|
12
|
+
// NULL is a category, but `IN (…, NULL)` never matches it: NULL = NULL is
|
|
13
|
+
// not true. It goes beside the list as IS NULL instead.
|
|
14
|
+
const values = tuples.map((t) => t[0]);
|
|
15
|
+
const real = values.filter((v) => v !== null);
|
|
16
|
+
const hasNull = real.length < values.length;
|
|
17
|
+
const inList = real.length > 0
|
|
18
|
+
? isIn(exprs[0], real.map((v) => literal(v)))
|
|
19
|
+
: null;
|
|
20
|
+
const isNull = hasNull ? isNotDistinct(exprs[0], literal(null)) : null;
|
|
21
|
+
if (inList && isNull)
|
|
22
|
+
return or([inList, isNull]);
|
|
23
|
+
return inList ?? isNull ?? isIn(exprs[0], []);
|
|
13
24
|
}
|
|
14
25
|
const clauses = tuples.map((t) => sqlAnd(t.map((v, i) => isNotDistinct(exprs[i], literal(v)))));
|
|
15
26
|
return clauses.length === 1 ? clauses[0] : or(clauses);
|
|
16
27
|
}
|
|
28
|
+
/**
|
|
29
|
+
* Above this many ids, a selection over an id column is a bitmap, not an `IN`
|
|
30
|
+
* list (see idBitmapPredicate). The crossover measured in DuckDB-WASM 1.30 lies
|
|
31
|
+
* between 500 values (`IN`, 29 ms on 2 M rows) and the bitmap (26 ms).
|
|
32
|
+
*/
|
|
33
|
+
export const BITMAP_MIN_IDS = 256;
|
|
34
|
+
/**
|
|
35
|
+
* `expr`'s id is one of `ids`, as one constant-size literal:
|
|
36
|
+
* `get_bit('<bits>'::BIT, <index>) = 1`.
|
|
37
|
+
*
|
|
38
|
+
* Never `NOT IN`, whatever the selection's size: DuckDB evaluates it as a chain
|
|
39
|
+
* of `<>`, 2.8 s on 2 M rows for 1 000 ids. And never a long `IN`, which it
|
|
40
|
+
* spends most of its time parsing.
|
|
41
|
+
*
|
|
42
|
+
* Bit `id + 1` is set for each selected id; bit 0 is id -1 ("not in the dim
|
|
43
|
+
* yet") and is never set; the last bit, `size + 1`, is a sentinel kept 0. The
|
|
44
|
+
* index is clamped onto [0, sentinel], so an id appended to the dim after the
|
|
45
|
+
* selection was made reads the sentinel (it was not selected: a snapshot), and
|
|
46
|
+
* get_bit never sees an index out of range, which is an error rather than a 0.
|
|
47
|
+
*/
|
|
48
|
+
export function idBitmapPredicate(expr, ids, size) {
|
|
49
|
+
const bits = new Array(size + 2).fill('0');
|
|
50
|
+
for (const id of ids)
|
|
51
|
+
if (id >= 0 && id < size)
|
|
52
|
+
bits[id + 1] = '1';
|
|
53
|
+
const sentinel = size + 1;
|
|
54
|
+
return sqlTag `get_bit(${literal(bits.join(''))}::BIT, CAST(greatest(least(${expr} + 1, ${sentinel}), 0) AS INTEGER)) = 1`;
|
|
55
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
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
|
+
* `trailing`: the last slot is not an id but a slot of its own that stays
|
|
90
|
+
* last (FieldActivity's free slot). Its fade moves to the new end, and the
|
|
91
|
+
* slot it leaves is a new id like any other, rather than inheriting it.
|
|
92
|
+
*/
|
|
93
|
+
resize(count: number, active?: readonly number[] | null, trailing?: boolean): void;
|
|
94
|
+
/**
|
|
95
|
+
* Select exactly these ids; every other id fades out. Ids outside the table
|
|
96
|
+
* are ignored. Does nothing when the selection is the one already headed to.
|
|
97
|
+
*/
|
|
98
|
+
setActive(ids: Iterable<number>): void;
|
|
99
|
+
/** Nothing selected: every id fades back to active. */
|
|
100
|
+
clear(): void;
|
|
101
|
+
/** Leave the loop, for an owner being torn down. Later changes land at once. */
|
|
102
|
+
destroy(): void;
|
|
103
|
+
}
|
|
@@ -0,0 +1,220 @@
|
|
|
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
|
+
* `trailing`: the last slot is not an id but a slot of its own that stays
|
|
122
|
+
* last (FieldActivity's free slot). Its fade moves to the new end, and the
|
|
123
|
+
* slot it leaves is a new id like any other, rather than inheriting it.
|
|
124
|
+
*/
|
|
125
|
+
resize(count, active, trailing = false) {
|
|
126
|
+
if (count === this.#to.length)
|
|
127
|
+
return;
|
|
128
|
+
const oldLength = this.#to.length;
|
|
129
|
+
const tail = trailing && oldLength > 0 && count > 0;
|
|
130
|
+
const keep = tail ? Math.min(oldLength - 1, count - 1) : Math.min(oldLength, count);
|
|
131
|
+
const ids = tail ? count - 1 : count;
|
|
132
|
+
const selecting = active === undefined ? this.#selecting : active !== null;
|
|
133
|
+
const fill = selecting ? 0 : 1;
|
|
134
|
+
const grown = (old) => {
|
|
135
|
+
const next = new Float32Array(count);
|
|
136
|
+
next.fill(fill);
|
|
137
|
+
next.set(old.subarray(0, keep), 0);
|
|
138
|
+
if (active) {
|
|
139
|
+
for (const id of active)
|
|
140
|
+
if (id >= keep && id < ids)
|
|
141
|
+
next[id] = 1;
|
|
142
|
+
}
|
|
143
|
+
if (tail)
|
|
144
|
+
next[count - 1] = old[oldLength - 1];
|
|
145
|
+
return next;
|
|
146
|
+
};
|
|
147
|
+
this.#from = grown(this.#from);
|
|
148
|
+
this.#to = grown(this.#to);
|
|
149
|
+
this.#scratch = new Float32Array(count);
|
|
150
|
+
this.version++;
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Select exactly these ids; every other id fades out. Ids outside the table
|
|
154
|
+
* are ignored. Does nothing when the selection is the one already headed to.
|
|
155
|
+
*/
|
|
156
|
+
setActive(ids) {
|
|
157
|
+
const next = this.#scratch;
|
|
158
|
+
next.fill(0);
|
|
159
|
+
for (const id of ids) {
|
|
160
|
+
if (id >= 0 && id < next.length)
|
|
161
|
+
next[id] = 1;
|
|
162
|
+
}
|
|
163
|
+
this.#selecting = true;
|
|
164
|
+
this.#retarget(next);
|
|
165
|
+
}
|
|
166
|
+
/** Nothing selected: every id fades back to active. */
|
|
167
|
+
clear() {
|
|
168
|
+
const next = this.#scratch;
|
|
169
|
+
next.fill(1);
|
|
170
|
+
this.#selecting = false;
|
|
171
|
+
this.#retarget(next);
|
|
172
|
+
}
|
|
173
|
+
/** Leave the loop, for an owner being torn down. Later changes land at once. */
|
|
174
|
+
destroy() {
|
|
175
|
+
this.clock.stop();
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Fold, point every id at its new state, and wake the loop.
|
|
179
|
+
*
|
|
180
|
+
* Returns early when `next` equals `to`: the fade already in flight (or the
|
|
181
|
+
* rest already reached) is headed exactly there, and restarting it would
|
|
182
|
+
* re-fold and bump the version for nothing, on every filter update.
|
|
183
|
+
*/
|
|
184
|
+
#retarget(next) {
|
|
185
|
+
const from = this.#from;
|
|
186
|
+
const to = this.#to;
|
|
187
|
+
let changed = false;
|
|
188
|
+
for (let id = 0; id < to.length; id++) {
|
|
189
|
+
if (to[id] !== next[id]) {
|
|
190
|
+
changed = true;
|
|
191
|
+
break;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
if (!changed)
|
|
195
|
+
return;
|
|
196
|
+
// THE FOLD: every id continues from where it is now, so a click landing
|
|
197
|
+
// mid-fade reverses or redirects it in place instead of jumping.
|
|
198
|
+
const t = this.clock.t;
|
|
199
|
+
for (let id = 0; id < to.length; id++) {
|
|
200
|
+
from[id] += (to[id] - from[id]) * t;
|
|
201
|
+
to[id] = next[id];
|
|
202
|
+
}
|
|
203
|
+
this.clock.restart();
|
|
204
|
+
this.version++;
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* Land exactly on `to`. A spring rests at an epsilon, and a table left at
|
|
208
|
+
* 0.0003 would start its next fold from a value that was never a state.
|
|
209
|
+
*/
|
|
210
|
+
#settle() {
|
|
211
|
+
this.clock.settle();
|
|
212
|
+
this.#from.set(this.#to);
|
|
213
|
+
}
|
|
214
|
+
#onClockRest() {
|
|
215
|
+
this.#settle();
|
|
216
|
+
this.version++;
|
|
217
|
+
// The last frame, at exactly t = 1, which the loop's final frame was not.
|
|
218
|
+
this.#onFrame?.();
|
|
219
|
+
}
|
|
220
|
+
}
|
|
@@ -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,26 @@ export declare class BandScaleState {
|
|
|
209
211
|
* undisplayed Other is ignored.
|
|
210
212
|
*/
|
|
211
213
|
setOtherId(id: number | null): void;
|
|
214
|
+
/** This axis's Other bucket id, while a mark on it has one; null otherwise. */
|
|
215
|
+
get otherId(): number | null;
|
|
216
|
+
/**
|
|
217
|
+
* This axis's Other bucket, shared by every mark on it that has one.
|
|
218
|
+
*
|
|
219
|
+
* One id per band scale, not per mark or per chart: marks share a band scale
|
|
220
|
+
* only when they agree on what Other is — the scale's key carries the table
|
|
221
|
+
* and the category-reducing transform (its `transformKey`), N included — so
|
|
222
|
+
* everything drawn on one axis lines up in one Other band. A bar and its
|
|
223
|
+
* value labels, or two charts with the identical topN, share it.
|
|
224
|
+
*
|
|
225
|
+
* Reference-counted by mark id. The first holder acquires; holders arriving
|
|
226
|
+
* while that is in flight share it; the last to release gives it back.
|
|
227
|
+
*/
|
|
228
|
+
retainOther(holder: string, acquire: () => Promise<SyntheticId>): Promise<SyntheticId>;
|
|
229
|
+
/**
|
|
230
|
+
* Drop `holder`'s claim on the Other bucket. The last one out hands the id to
|
|
231
|
+
* `release` — once the acquire has landed, if it is still in flight.
|
|
232
|
+
*/
|
|
233
|
+
releaseOther(holder: string, release: (other: SyntheticId) => void): void;
|
|
212
234
|
/**
|
|
213
235
|
* Identity id currently displayed at `slot`, or null when the slot holds no
|
|
214
236
|
* category. Use this to compare a display position — what invertScreenToData
|
|
@@ -311,7 +333,7 @@ export declare class BandScaleState {
|
|
|
311
333
|
/**
|
|
312
334
|
* Get D3 band scale configured for rendering
|
|
313
335
|
*/
|
|
314
|
-
getBaseScale(
|
|
336
|
+
getBaseScale(_axis: string, innerWidthOrHeight: number): ScaleBand<DimValue>;
|
|
315
337
|
/**
|
|
316
338
|
* Update scale domain (for D3 scale)
|
|
317
339
|
*/
|
|
@@ -375,26 +397,30 @@ export declare class BandScaleState {
|
|
|
375
397
|
* so it fades out over the half slot before that centre reaches the axis end
|
|
376
398
|
* (the animated count), and is gone from there; the same at the start. An
|
|
377
399
|
* entrant's label appears as its band crosses in.
|
|
400
|
+
*
|
|
401
|
+
* A selection's dimming is not here: it belongs to the filter groups of the
|
|
402
|
+
* marks the axis labels, so the axis multiplies it in (BandAxisTicks).
|
|
378
403
|
*/
|
|
379
404
|
tickOpacity(key: number): number;
|
|
380
405
|
/**
|
|
381
406
|
* Where a tick's label sits now, in pixels along the axis: the centre of its
|
|
382
407
|
* category's current slot, so it travels with the bar. Slot 0 is at the start
|
|
383
|
-
* of the range on
|
|
384
|
-
*
|
|
408
|
+
* of the range on both axes: the left on x, the top on y, as in the shader
|
|
409
|
+
* and d3's range. Per frame, O(1).
|
|
385
410
|
*/
|
|
386
|
-
tickPosition(key: number, rangeSize: number
|
|
411
|
+
tickPosition(key: number, rangeSize: number): number;
|
|
387
412
|
/**
|
|
388
413
|
* The same, for a caller that already has the slot.
|
|
389
414
|
*
|
|
390
415
|
* A consumer that has to test the slot first — an SVG label, which must not
|
|
391
416
|
* draw a category the axis is not displaying — would otherwise ask for it
|
|
392
|
-
* twice per item per frame.
|
|
393
|
-
*
|
|
417
|
+
* twice per item per frame.
|
|
418
|
+
*
|
|
419
|
+
* The same on both axes. A y band axis reads top-down, like a list: slot 0
|
|
420
|
+
* at the top, so a ranking and an explicit domain need no reversing, and
|
|
421
|
+
* the exit one past the last slot is the end of the list on either axis.
|
|
394
422
|
*/
|
|
395
|
-
slotPosition(slot: number, rangeSize: number
|
|
396
|
-
/** Whether a tick's category is outside the current selection. */
|
|
397
|
-
isTickDimmed(tick: BandTick): boolean;
|
|
423
|
+
slotPosition(slot: number, rangeSize: number): number;
|
|
398
424
|
/**
|
|
399
425
|
* Centre of a (possibly fractional) slot, measured from the start of the
|
|
400
426
|
* range — the same layout the shader's computeBandLayout uses, so a label
|
|
@@ -431,7 +457,7 @@ export declare class BandScaleState {
|
|
|
431
457
|
* Returns the 0-based index of the band under the cursor, or null if
|
|
432
458
|
* no bands exist.
|
|
433
459
|
*/
|
|
434
|
-
invertScreenToData(screenVal: number, rangeSize: number,
|
|
460
|
+
invertScreenToData(screenVal: number, rangeSize: number, _isYAxis?: boolean): number | null;
|
|
435
461
|
/**
|
|
436
462
|
* Get all mark extents (for debugging). Ids when the scale has identity.
|
|
437
463
|
*/
|