@genome-spy/core 0.43.2 → 0.44.0
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/bundle/index.es.js +5621 -5346
- package/dist/bundle/index.js +156 -104
- package/dist/schema.json +412 -43
- package/dist/src/data/sources/lazy/axisTickSource.d.ts +1 -1
- package/dist/src/data/sources/lazy/axisTickSource.d.ts.map +1 -1
- package/dist/src/data/sources/lazy/axisTickSource.js +2 -2
- package/dist/src/data/sources/lazy/bigBedSource.js +70 -28
- package/dist/src/data/sources/lazy/bigWigSource.d.ts +6 -0
- package/dist/src/data/sources/lazy/bigWigSource.d.ts.map +1 -1
- package/dist/src/data/sources/lazy/bigWigSource.js +3 -5
- package/dist/src/data/sources/lazy/singleAxisLazySource.d.ts +1 -1
- package/dist/src/data/sources/lazy/singleAxisLazySource.d.ts.map +1 -1
- package/dist/src/data/sources/lazy/singleAxisLazySource.js +1 -1
- package/dist/src/data/sources/lazy/singleAxisWindowedSource.d.ts +7 -12
- package/dist/src/data/sources/lazy/singleAxisWindowedSource.d.ts.map +1 -1
- package/dist/src/data/sources/lazy/singleAxisWindowedSource.js +33 -29
- package/dist/src/data/transforms/filterScoredLabels.js +1 -1
- package/dist/src/encoder/encoder.d.ts.map +1 -1
- package/dist/src/encoder/encoder.js +16 -6
- package/dist/src/genomeSpy.d.ts +1 -0
- package/dist/src/genomeSpy.d.ts.map +1 -1
- package/dist/src/genomeSpy.js +108 -6
- package/dist/src/gl/glslScaleGenerator.d.ts +23 -3
- package/dist/src/gl/glslScaleGenerator.d.ts.map +1 -1
- package/dist/src/gl/glslScaleGenerator.js +137 -42
- package/dist/src/gl/webGLHelper.d.ts.map +1 -1
- package/dist/src/gl/webGLHelper.js +5 -7
- package/dist/src/marks/link.common.glsl.js +2 -0
- package/dist/src/marks/link.d.ts.map +1 -1
- package/dist/src/marks/link.js +19 -9
- package/dist/src/marks/link.vertex.glsl.js +1 -1
- package/dist/src/marks/mark.d.ts +19 -17
- package/dist/src/marks/mark.d.ts.map +1 -1
- package/dist/src/marks/mark.js +181 -120
- package/dist/src/marks/point.common.glsl.js +1 -1
- package/dist/src/marks/rect.common.glsl.js +2 -0
- package/dist/src/marks/rect.d.ts.map +1 -1
- package/dist/src/marks/rect.js +12 -12
- package/dist/src/marks/rect.vertex.glsl.js +1 -1
- package/dist/src/marks/rule.common.glsl.js +1 -1
- package/dist/src/marks/rule.js +2 -2
- package/dist/src/marks/text.common.glsl.js +1 -1
- package/dist/src/marks/text.js +2 -2
- package/dist/src/paramBroker.d.ts +19 -3
- package/dist/src/paramBroker.d.ts.map +1 -1
- package/dist/src/paramBroker.js +18 -2
- package/dist/src/spec/channel.d.ts +4 -3
- package/dist/src/spec/mark.d.ts +17 -25
- package/dist/src/spec/parameter.d.ts +123 -0
- package/dist/src/spec/root.d.ts +9 -0
- package/dist/src/spec/scale.d.ts +2 -1
- package/dist/src/spec/view.d.ts +1 -1
- package/dist/src/types/scaleResolutionApi.d.ts +7 -3
- package/dist/src/utils/expression.d.ts +2 -2
- package/dist/src/utils/expression.d.ts.map +1 -1
- package/dist/src/utils/expression.js +3 -3
- package/dist/src/view/axisView.js +3 -3
- package/dist/src/view/scaleResolution.d.ts +8 -18
- package/dist/src/view/scaleResolution.d.ts.map +1 -1
- package/dist/src/view/scaleResolution.js +220 -126
- package/dist/src/view/scaleResolution.test.js +7 -7
- package/dist/src/view/unitView.d.ts.map +1 -1
- package/dist/src/view/unitView.js +10 -3
- package/dist/src/view/view.js +2 -2
- package/package.json +2 -2
|
@@ -20,7 +20,6 @@ import { scale as vegaScale, isDiscrete, isContinuous } from "vega-scale";
|
|
|
20
20
|
import mergeObjects from "../utils/mergeObjects.js";
|
|
21
21
|
import createScale, { configureScale } from "../scale/scale.js";
|
|
22
22
|
|
|
23
|
-
import { invalidate, getCachedOrCall } from "../utils/propertyCacher.js";
|
|
24
23
|
import {
|
|
25
24
|
getChannelDefWithScale,
|
|
26
25
|
isColorChannel,
|
|
@@ -37,6 +36,7 @@ import { NominalDomain } from "../utils/domainArray.js";
|
|
|
37
36
|
import { easeCubicInOut } from "d3-ease";
|
|
38
37
|
import { asArray, shallowArrayEquals } from "../utils/arrayUtils.js";
|
|
39
38
|
import eerp from "../utils/eerp.js";
|
|
39
|
+
import { isExprRef } from "../marks/mark.js";
|
|
40
40
|
|
|
41
41
|
// Register scaleLocus to Vega-Scale.
|
|
42
42
|
// Loci are discrete but the scale's domain can be adjusted in a continuous manner.
|
|
@@ -66,6 +66,7 @@ export const INDEX = "index";
|
|
|
66
66
|
export default class ScaleResolution {
|
|
67
67
|
/**
|
|
68
68
|
* @typedef {import("../types/scaleResolutionApi.js").ScaleResolutionApi} ScaleResolutionApi
|
|
69
|
+
* @typedef {import("../types/scaleResolutionApi.js").ScaleResolutionEventType} ScaleResolutionEventType
|
|
69
70
|
* @typedef {import("../spec/channel.js").Channel} Channel
|
|
70
71
|
* @typedef {import("../spec/channel.js").ChannelWithScale} ChannelWithScale
|
|
71
72
|
* @typedef {import("../spec/scale.js").NumericDomain} NumericDomain
|
|
@@ -77,16 +78,31 @@ export default class ScaleResolution {
|
|
|
77
78
|
* @typedef {import("../utils/domainArray.js").DomainArray} DomainArray
|
|
78
79
|
* @typedef {import("../genome/genome.js").ChromosomalLocus} ChromosomalLocus
|
|
79
80
|
* @typedef {import("../types/scaleResolutionApi.js").ScaleResolutionListener} ScaleResolutionListener
|
|
81
|
+
*
|
|
82
|
+
* @typedef {VegaScale & { props: import("../spec/scale.js").Scale }} ScaleWithProps
|
|
80
83
|
*/
|
|
81
84
|
|
|
82
85
|
/** @type {number[]} */
|
|
83
|
-
#zoomExtent
|
|
86
|
+
#zoomExtent;
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* @type {Record<ScaleResolutionEventType, Set<ScaleResolutionListener>>}
|
|
90
|
+
*/
|
|
91
|
+
#listeners = {
|
|
92
|
+
domain: new Set(),
|
|
93
|
+
range: new Set(),
|
|
94
|
+
};
|
|
84
95
|
|
|
85
|
-
/** @type {
|
|
86
|
-
#
|
|
96
|
+
/** @type {ScaleWithProps} */
|
|
97
|
+
#scale;
|
|
87
98
|
|
|
88
|
-
/**
|
|
89
|
-
|
|
99
|
+
/**
|
|
100
|
+
* Keeps track of the expression references in the range. If range is modified,
|
|
101
|
+
* new expressions are created and the old ones must be invalidated.
|
|
102
|
+
*
|
|
103
|
+
* @type {Set<import("../paramBroker.js").ExprRefFunction>}
|
|
104
|
+
*/
|
|
105
|
+
#rangeExprRefListeners = new Set();
|
|
90
106
|
|
|
91
107
|
/**
|
|
92
108
|
* @param {Channel} channel
|
|
@@ -102,36 +118,37 @@ export default class ScaleResolution {
|
|
|
102
118
|
this.name = undefined;
|
|
103
119
|
}
|
|
104
120
|
|
|
121
|
+
get #viewContext() {
|
|
122
|
+
return this.members[0].view.context;
|
|
123
|
+
}
|
|
124
|
+
|
|
105
125
|
/**
|
|
106
126
|
* Adds a listener that is called when the scale domain is changed,
|
|
107
127
|
* e.g., zoomed. The call is synchronous and happens before the views
|
|
108
128
|
* are rendered.
|
|
109
129
|
*
|
|
110
|
-
* @param {
|
|
130
|
+
* @param {ScaleResolutionEventType} type
|
|
111
131
|
* @param {ScaleResolutionListener} listener function
|
|
112
132
|
*/
|
|
113
133
|
addEventListener(type, listener) {
|
|
114
|
-
|
|
115
|
-
throw new Error("Unsupported event type: " + type);
|
|
116
|
-
}
|
|
117
|
-
this.#domainListeners.add(listener);
|
|
134
|
+
this.#listeners[type].add(listener);
|
|
118
135
|
}
|
|
119
136
|
|
|
120
137
|
/**
|
|
121
|
-
* @param {
|
|
138
|
+
* @param {ScaleResolutionEventType} type
|
|
122
139
|
* @param {ScaleResolutionListener} listener function
|
|
123
140
|
*/
|
|
124
141
|
removeEventListener(type, listener) {
|
|
125
|
-
|
|
126
|
-
throw new Error("Unsupported event type: " + type);
|
|
127
|
-
}
|
|
128
|
-
this.#domainListeners.delete(listener);
|
|
142
|
+
this.#listeners[type].delete(listener);
|
|
129
143
|
}
|
|
130
144
|
|
|
131
|
-
|
|
132
|
-
|
|
145
|
+
/**
|
|
146
|
+
* @param {ScaleResolutionEventType} type
|
|
147
|
+
*/
|
|
148
|
+
#notifyListeners(type) {
|
|
149
|
+
for (const listener of this.#listeners[type].values()) {
|
|
133
150
|
listener({
|
|
134
|
-
type
|
|
151
|
+
type,
|
|
135
152
|
scaleResolution: this,
|
|
136
153
|
});
|
|
137
154
|
}
|
|
@@ -175,11 +192,11 @@ export default class ScaleResolution {
|
|
|
175
192
|
/**
|
|
176
193
|
* Returns true if the domain has been defined explicitly, i.e. not extracted from the data.
|
|
177
194
|
*/
|
|
178
|
-
isExplicitDomain() {
|
|
195
|
+
#isExplicitDomain() {
|
|
179
196
|
return !!this.getConfiguredDomain();
|
|
180
197
|
}
|
|
181
198
|
|
|
182
|
-
isDomainInitialized() {
|
|
199
|
+
#isDomainInitialized() {
|
|
183
200
|
const s = this.#scale;
|
|
184
201
|
if (!s) {
|
|
185
202
|
return false;
|
|
@@ -205,18 +222,15 @@ export default class ScaleResolution {
|
|
|
205
222
|
* @returns {import("../spec/scale.js").Scale}
|
|
206
223
|
*/
|
|
207
224
|
#getMergedScaleProps() {
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
(member)
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
)
|
|
215
|
-
.filter((props) => props !== undefined);
|
|
225
|
+
const propArray = this.members
|
|
226
|
+
.map(
|
|
227
|
+
(member) =>
|
|
228
|
+
getChannelDefWithScale(member.view, member.channel).scale
|
|
229
|
+
)
|
|
230
|
+
.filter((props) => props !== undefined);
|
|
216
231
|
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
});
|
|
232
|
+
// TODO: Disabled scale: https://vega.github.io/vega-lite/docs/scale.html#disable
|
|
233
|
+
return mergeObjects(propArray, "scale", ["domain"]);
|
|
220
234
|
}
|
|
221
235
|
|
|
222
236
|
/**
|
|
@@ -225,67 +239,120 @@ export default class ScaleResolution {
|
|
|
225
239
|
*
|
|
226
240
|
* @returns {import("../spec/scale.js").Scale}
|
|
227
241
|
*/
|
|
228
|
-
getScaleProps() {
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
return { type: "null" };
|
|
236
|
-
}
|
|
242
|
+
#getScaleProps() {
|
|
243
|
+
const mergedProps = this.#getMergedScaleProps();
|
|
244
|
+
if (mergedProps === null || mergedProps.type == "null") {
|
|
245
|
+
// No scale (pass-thru)
|
|
246
|
+
// TODO: Check that the channel is compatible
|
|
247
|
+
return { type: "null" };
|
|
248
|
+
}
|
|
237
249
|
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
250
|
+
const props = {
|
|
251
|
+
...this.#getDefaultScaleProperties(this.type),
|
|
252
|
+
...mergedProps,
|
|
253
|
+
};
|
|
242
254
|
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
255
|
+
if (!props.type) {
|
|
256
|
+
props.type = getDefaultScaleType(this.channel, this.type);
|
|
257
|
+
}
|
|
246
258
|
|
|
247
|
-
|
|
259
|
+
const domain = this.#getInitialDomain();
|
|
248
260
|
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
261
|
+
if (domain && domain.length > 0) {
|
|
262
|
+
props.domain = domain;
|
|
263
|
+
} else if (isDiscrete(props.type)) {
|
|
264
|
+
props.domain = new NominalDomain();
|
|
265
|
+
}
|
|
254
266
|
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
267
|
+
if (!props.domain && props.domainMid !== undefined) {
|
|
268
|
+
// Initialize with a bogus domain so that scale.js can inject the domainMid.
|
|
269
|
+
// The number of domain elements must be know before the glsl scale is generated.
|
|
270
|
+
props.domain = [props.domainMin ?? 0, props.domainMax ?? 1];
|
|
271
|
+
}
|
|
260
272
|
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
273
|
+
// Reverse discrete y axis
|
|
274
|
+
if (
|
|
275
|
+
this.channel == "y" &&
|
|
276
|
+
isDiscrete(props.type) &&
|
|
277
|
+
props.reverse == undefined
|
|
278
|
+
) {
|
|
279
|
+
props.reverse = true;
|
|
280
|
+
}
|
|
269
281
|
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
282
|
+
if (props.range && props.scheme) {
|
|
283
|
+
delete props.scheme;
|
|
284
|
+
// TODO: Props should be set more intelligently
|
|
285
|
+
/*
|
|
274
286
|
throw new Error(
|
|
275
287
|
`Scale has both "range" and "scheme" defined! Views: ${this._getViewPaths()}`
|
|
276
288
|
);
|
|
277
289
|
*/
|
|
278
|
-
|
|
290
|
+
}
|
|
279
291
|
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
292
|
+
// By default, index and locus scales are zoomable, others are not
|
|
293
|
+
if (!("zoom" in props) && ["index", "locus"].includes(props.type)) {
|
|
294
|
+
props.zoom = true;
|
|
295
|
+
}
|
|
284
296
|
|
|
285
|
-
|
|
297
|
+
applyLockedProperties(props, this.channel);
|
|
286
298
|
|
|
287
|
-
|
|
288
|
-
|
|
299
|
+
return props;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
/**
|
|
303
|
+
* Configures range. If range is an array of expressions, they are evaluated
|
|
304
|
+
* and the scale is updated when the expressions change.
|
|
305
|
+
*/
|
|
306
|
+
#configureRange() {
|
|
307
|
+
const props = this.#scale.props;
|
|
308
|
+
const range = props.range;
|
|
309
|
+
this.#rangeExprRefListeners.forEach((fn) => fn.invalidate());
|
|
310
|
+
|
|
311
|
+
if (!range || !isArray(range)) {
|
|
312
|
+
// Named ranges?
|
|
313
|
+
return;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
/**
|
|
317
|
+
* @param {T} array
|
|
318
|
+
* @param {boolean} reverse
|
|
319
|
+
* @returns {T}
|
|
320
|
+
* @template T
|
|
321
|
+
*/
|
|
322
|
+
const flip = (array, reverse) =>
|
|
323
|
+
// @ts-ignore TODO: Fix the type (should be a generic union array type)
|
|
324
|
+
reverse ? array.slice().reverse() : array;
|
|
325
|
+
|
|
326
|
+
if (range.some(isExprRef)) {
|
|
327
|
+
/** @type {(() => void)[]} */
|
|
328
|
+
let expressions;
|
|
329
|
+
|
|
330
|
+
const evaluateAndSet = () => {
|
|
331
|
+
this.#scale.range(
|
|
332
|
+
flip(
|
|
333
|
+
expressions.map((expr) => expr()),
|
|
334
|
+
props.reverse
|
|
335
|
+
)
|
|
336
|
+
);
|
|
337
|
+
};
|
|
338
|
+
|
|
339
|
+
expressions = range.map((elem) => {
|
|
340
|
+
if (isExprRef(elem)) {
|
|
341
|
+
const fn = this.#viewContext.paramBroker.createExpression(
|
|
342
|
+
elem.expr
|
|
343
|
+
);
|
|
344
|
+
fn.addListener(evaluateAndSet);
|
|
345
|
+
this.#rangeExprRefListeners.add(fn);
|
|
346
|
+
return () => fn(null);
|
|
347
|
+
} else {
|
|
348
|
+
return () => elem;
|
|
349
|
+
}
|
|
350
|
+
});
|
|
351
|
+
|
|
352
|
+
evaluateAndSet();
|
|
353
|
+
} else {
|
|
354
|
+
this.#scale.range(flip(range, props.reverse));
|
|
355
|
+
}
|
|
289
356
|
}
|
|
290
357
|
|
|
291
358
|
#getInitialDomain() {
|
|
@@ -330,53 +397,64 @@ export default class ScaleResolution {
|
|
|
330
397
|
* Reconfigures the scale: updates domain and other settings
|
|
331
398
|
*/
|
|
332
399
|
reconfigure() {
|
|
333
|
-
|
|
334
|
-
const domainWasInitialized = this.isDomainInitialized();
|
|
400
|
+
const scale = this.#scale;
|
|
335
401
|
|
|
336
|
-
|
|
402
|
+
if (!scale || scale.type == "null") {
|
|
403
|
+
return;
|
|
404
|
+
}
|
|
337
405
|
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
configureScale(props, this.#scale);
|
|
341
|
-
if (isContinuous(this.#scale.type)) {
|
|
342
|
-
this.#zoomExtent = this.#getZoomExtent();
|
|
343
|
-
}
|
|
406
|
+
const domainWasInitialized = this.#isDomainInitialized();
|
|
407
|
+
const previousDomain = scale.domain();
|
|
344
408
|
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
return;
|
|
348
|
-
}
|
|
409
|
+
const props = this.#getScaleProps();
|
|
410
|
+
configureScale({ ...props, range: undefined }, scale);
|
|
349
411
|
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
412
|
+
// Annotate the scale with the new props
|
|
413
|
+
scale.props = props;
|
|
414
|
+
this.#configureRange();
|
|
415
|
+
|
|
416
|
+
if (isContinuous(scale.type)) {
|
|
417
|
+
this.#zoomExtent = this.#getZoomExtent();
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
if (!domainWasInitialized) {
|
|
421
|
+
this.#notifyListeners("domain");
|
|
422
|
+
return;
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
const newDomain = scale.domain();
|
|
426
|
+
if (!shallowArrayEquals(newDomain, previousDomain)) {
|
|
427
|
+
if (this.isZoomable()) {
|
|
428
|
+
// Don't mess with zoomed views, restore the previous domain
|
|
429
|
+
scale.domain(previousDomain);
|
|
430
|
+
} else if (this.#isZoomingSupported()) {
|
|
431
|
+
// It can be zoomed, so lets make a smooth transition.
|
|
432
|
+
// Restore the previous domain and zoom smoothly to the new domain.
|
|
433
|
+
scale.domain(previousDomain);
|
|
434
|
+
this.zoomTo(newDomain, 500); // TODO: Configurable duration
|
|
435
|
+
} else {
|
|
436
|
+
// Update immediately if the previous domain was the initial domain [0, 0]
|
|
437
|
+
this.#notifyListeners("domain");
|
|
364
438
|
}
|
|
365
439
|
}
|
|
366
440
|
}
|
|
367
441
|
|
|
368
442
|
/**
|
|
369
|
-
* @returns {
|
|
443
|
+
* @returns {ScaleWithProps}
|
|
370
444
|
*/
|
|
371
|
-
|
|
445
|
+
get scale() {
|
|
372
446
|
if (this.#scale) {
|
|
373
447
|
return this.#scale;
|
|
374
448
|
}
|
|
375
449
|
|
|
376
|
-
const props = this
|
|
450
|
+
const props = this.#getScaleProps();
|
|
451
|
+
|
|
452
|
+
const scale = createScale({ ...props, range: undefined });
|
|
453
|
+
// Annotate the scale with props
|
|
454
|
+
scale.props = props;
|
|
377
455
|
|
|
378
|
-
const scale = createScale(props);
|
|
379
456
|
this.#scale = scale;
|
|
457
|
+
this.#configureRange();
|
|
380
458
|
|
|
381
459
|
if (isScaleLocus(scale)) {
|
|
382
460
|
scale.genome(this.getGenome());
|
|
@@ -386,11 +464,27 @@ export default class ScaleResolution {
|
|
|
386
464
|
this.#zoomExtent = this.#getZoomExtent();
|
|
387
465
|
}
|
|
388
466
|
|
|
467
|
+
// Hijack the range method
|
|
468
|
+
const range = scale.range;
|
|
469
|
+
if (range) {
|
|
470
|
+
const notify = () => this.#notifyListeners("range");
|
|
471
|
+
scale.range = function (/** @type {any} */ _) {
|
|
472
|
+
if (arguments.length) {
|
|
473
|
+
range(_);
|
|
474
|
+
notify();
|
|
475
|
+
} else {
|
|
476
|
+
return range();
|
|
477
|
+
}
|
|
478
|
+
};
|
|
479
|
+
// The initial setting
|
|
480
|
+
notify();
|
|
481
|
+
}
|
|
482
|
+
|
|
389
483
|
return scale;
|
|
390
484
|
}
|
|
391
485
|
|
|
392
486
|
getDomain() {
|
|
393
|
-
return this.
|
|
487
|
+
return this.scale.domain();
|
|
394
488
|
}
|
|
395
489
|
|
|
396
490
|
/**
|
|
@@ -420,14 +514,14 @@ export default class ScaleResolution {
|
|
|
420
514
|
*/
|
|
421
515
|
isZoomable() {
|
|
422
516
|
// Check explicit configuration
|
|
423
|
-
return this.#isZoomingSupported() && !!this.
|
|
517
|
+
return this.#isZoomingSupported() && !!this.scale.props.zoom;
|
|
424
518
|
}
|
|
425
519
|
|
|
426
520
|
/**
|
|
427
521
|
* Returns true if zooming is supported but not necessarily allowed in view spec.
|
|
428
522
|
*/
|
|
429
523
|
#isZoomingSupported() {
|
|
430
|
-
const type = this.
|
|
524
|
+
const type = this.scale.type;
|
|
431
525
|
return isContinuous(type);
|
|
432
526
|
}
|
|
433
527
|
|
|
@@ -444,7 +538,7 @@ export default class ScaleResolution {
|
|
|
444
538
|
return false;
|
|
445
539
|
}
|
|
446
540
|
|
|
447
|
-
const scale = this.
|
|
541
|
+
const scale = this.scale;
|
|
448
542
|
const oldDomain = scale.domain();
|
|
449
543
|
let newDomain = [...oldDomain];
|
|
450
544
|
|
|
@@ -452,7 +546,7 @@ export default class ScaleResolution {
|
|
|
452
546
|
// @ts-ignore
|
|
453
547
|
let anchor = scale.invert(scaleAnchor);
|
|
454
548
|
|
|
455
|
-
if (
|
|
549
|
+
if (scale.props.reverse) {
|
|
456
550
|
pan = -pan;
|
|
457
551
|
}
|
|
458
552
|
|
|
@@ -504,7 +598,7 @@ export default class ScaleResolution {
|
|
|
504
598
|
|
|
505
599
|
if ([0, 1].some((i) => newDomain[i] != oldDomain[i])) {
|
|
506
600
|
scale.domain(newDomain);
|
|
507
|
-
this.#
|
|
601
|
+
this.#notifyListeners("domain");
|
|
508
602
|
return true;
|
|
509
603
|
}
|
|
510
604
|
|
|
@@ -533,7 +627,7 @@ export default class ScaleResolution {
|
|
|
533
627
|
|
|
534
628
|
const animator = this.members[0]?.view.context.animator;
|
|
535
629
|
|
|
536
|
-
const scale = this.
|
|
630
|
+
const scale = this.scale;
|
|
537
631
|
const from = /** @type {number[]} */ (scale.domain());
|
|
538
632
|
|
|
539
633
|
if (duration > 0 && from.length == 2) {
|
|
@@ -552,16 +646,16 @@ export default class ScaleResolution {
|
|
|
552
646
|
const wt = (fw - w) / (fw - tw);
|
|
553
647
|
const c = wt * tc + (1 - wt) * fc;
|
|
554
648
|
scale.domain([c - w / 2, c + w / 2]);
|
|
555
|
-
this.#
|
|
649
|
+
this.#notifyListeners("domain");
|
|
556
650
|
},
|
|
557
651
|
});
|
|
558
652
|
|
|
559
653
|
scale.domain(to);
|
|
560
|
-
this.#
|
|
654
|
+
this.#notifyListeners("domain");
|
|
561
655
|
} else {
|
|
562
656
|
scale.domain(to);
|
|
563
657
|
animator?.requestRender();
|
|
564
|
-
this.#
|
|
658
|
+
this.#notifyListeners("domain");
|
|
565
659
|
}
|
|
566
660
|
}
|
|
567
661
|
|
|
@@ -580,7 +674,7 @@ export default class ScaleResolution {
|
|
|
580
674
|
|
|
581
675
|
if ([0, 1].some((i) => newDomain[i] != oldDomain[i])) {
|
|
582
676
|
this.#scale.domain(newDomain);
|
|
583
|
-
this.#
|
|
677
|
+
this.#notifyListeners("domain");
|
|
584
678
|
return true;
|
|
585
679
|
}
|
|
586
680
|
return false;
|
|
@@ -595,14 +689,14 @@ export default class ScaleResolution {
|
|
|
595
689
|
getZoomLevel() {
|
|
596
690
|
// Zoom level makes sense only for user-zoomable scales where zoom extent is defined
|
|
597
691
|
if (this.isZoomable()) {
|
|
598
|
-
return span(this.#zoomExtent) / span(this.
|
|
692
|
+
return span(this.#zoomExtent) / span(this.scale.domain());
|
|
599
693
|
}
|
|
600
694
|
|
|
601
695
|
return 1.0;
|
|
602
696
|
}
|
|
603
697
|
|
|
604
698
|
#getZoomExtent() {
|
|
605
|
-
const props = this.
|
|
699
|
+
const props = this.scale.props;
|
|
606
700
|
const zoom = props.zoom;
|
|
607
701
|
|
|
608
702
|
if (isZoomParams(zoom)) {
|
|
@@ -632,12 +726,12 @@ export default class ScaleResolution {
|
|
|
632
726
|
const channel = this.channel;
|
|
633
727
|
const props = {};
|
|
634
728
|
|
|
635
|
-
if (this
|
|
729
|
+
if (this.#isExplicitDomain()) {
|
|
636
730
|
props.zero = false;
|
|
637
731
|
}
|
|
638
732
|
|
|
639
733
|
if (isPositionalChannel(channel)) {
|
|
640
|
-
props.nice = !this
|
|
734
|
+
props.nice = !this.#isExplicitDomain();
|
|
641
735
|
} else if (isColorChannel(channel)) {
|
|
642
736
|
// TODO: Named ranges
|
|
643
737
|
props.scheme =
|
|
@@ -687,7 +781,7 @@ export default class ScaleResolution {
|
|
|
687
781
|
* @param {number} value
|
|
688
782
|
*/
|
|
689
783
|
invertToComplex(value) {
|
|
690
|
-
const scale = this.
|
|
784
|
+
const scale = this.scale;
|
|
691
785
|
if ("invert" in scale) {
|
|
692
786
|
const inverted = /** @type {number} */ (scale.invert(value));
|
|
693
787
|
return this.toComplex(inverted);
|
|
@@ -335,7 +335,7 @@ describe("Domain handling", () => {
|
|
|
335
335
|
);
|
|
336
336
|
|
|
337
337
|
/** @param {import("./view.js").default} view */
|
|
338
|
-
const d = (view) => view.getScaleResolution("y").
|
|
338
|
+
const d = (view) => view.getScaleResolution("y").scale.domain();
|
|
339
339
|
|
|
340
340
|
expect(r(d(view))).toEqual([1, 5]);
|
|
341
341
|
expect(r(d(view.children[0]))).toEqual([1, 5]);
|
|
@@ -371,7 +371,7 @@ describe("Domain handling", () => {
|
|
|
371
371
|
);
|
|
372
372
|
|
|
373
373
|
/** @param {import("./view.js").default} view */
|
|
374
|
-
const d = (view) => view.getScaleResolution("y").
|
|
374
|
+
const d = (view) => view.getScaleResolution("y").scale.domain();
|
|
375
375
|
|
|
376
376
|
expect(r(d(view))).toEqual([1, 5]);
|
|
377
377
|
expect(r(d(view.children[0]))).toEqual([1, 5]);
|
|
@@ -403,7 +403,7 @@ describe("Domain handling", () => {
|
|
|
403
403
|
);
|
|
404
404
|
|
|
405
405
|
/** @param {import("./view.js").default} view */
|
|
406
|
-
const d = (view) => view.getScaleResolution("y").
|
|
406
|
+
const d = (view) => view.getScaleResolution("y").scale.domain();
|
|
407
407
|
|
|
408
408
|
// FAILS!!!!!!! TODO: FIX!!
|
|
409
409
|
// expect(r(d(view))).toEqual([1, 5]);
|
|
@@ -442,7 +442,7 @@ describe("Domain handling", () => {
|
|
|
442
442
|
);
|
|
443
443
|
|
|
444
444
|
/** @param {import("./view.js").default} view */
|
|
445
|
-
const d = (view) => view.getScaleResolution("y").
|
|
445
|
+
const d = (view) => view.getScaleResolution("y").scale.domain();
|
|
446
446
|
|
|
447
447
|
expect(r(d(view))).toEqual([1, 5]);
|
|
448
448
|
expect(r(d(view.children[0]))).toEqual([1, 5]);
|
|
@@ -469,7 +469,7 @@ describe("Domain handling", () => {
|
|
|
469
469
|
|
|
470
470
|
const d = /** @param {import("../spec/channel.js").Channel} channel*/ (
|
|
471
471
|
channel
|
|
472
|
-
) => view.getScaleResolution(channel).
|
|
472
|
+
) => view.getScaleResolution(channel).scale.domain();
|
|
473
473
|
|
|
474
474
|
expect(d("x")).toEqual([0, 3]);
|
|
475
475
|
expect(d("y")).toEqual([0, 3]);
|
|
@@ -497,7 +497,7 @@ describe("Domain handling", () => {
|
|
|
497
497
|
);
|
|
498
498
|
|
|
499
499
|
const d = /** @param {Channel} channel*/ (channel) =>
|
|
500
|
-
view.getScaleResolution(channel).
|
|
500
|
+
view.getScaleResolution(channel).scale.domain();
|
|
501
501
|
|
|
502
502
|
expect(d("x")).toEqual([1, 4]);
|
|
503
503
|
expect(d("x")).toEqual([1, 4]);
|
|
@@ -530,7 +530,7 @@ describe("Domain handling", () => {
|
|
|
530
530
|
}
|
|
531
531
|
|
|
532
532
|
const d = /** @param {Channel} channel*/ (channel) =>
|
|
533
|
-
view.getScaleResolution(channel).
|
|
533
|
+
view.getScaleResolution(channel).scale.domain();
|
|
534
534
|
|
|
535
535
|
expect(d("x")).toEqual([2, 3]);
|
|
536
536
|
expect(d("y")).toEqual([2, 3]);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"unitView.d.ts","sourceRoot":"","sources":["../../../src/view/unitView.js"],"names":[],"mappings":"AAqBA;;;;GAIG;AACH;QAHkB,MAAM,GAAE,cAAc,kBAAkB,EAAE,OAAO;EASjE;AAEF;IACI;;;;;;;OAOG;IACH;;;;;;;;OAQG;IACH,kBAPW,OAAO,iBAAiB,EAAE,QAAQ,WAClC,OAAO,yBAAyB,EAAE,OAAO,gBACzC,OAAO,oBAAoB,EAAE,OAAO,cACpC,OAAO,WAAW,EAAE,OAAO,QAC3B,MAAM,YACN,OAAO,WAAW,EAAE,WAAW,EAkBzC;IAbG,yCAAgB;IAIZ,iDAAiD;IACjD,MADW,OAAO,kBAAkB,EAAE,OAAO,CACnB;IAUlC;;;;OAIG;IACH,gBAJW,OAAO,4CAA4C,EAAE,OAAO,UAC5D,OAAO,uBAAuB,EAAE,OAAO,YACvC,OAAO,uBAAuB,EAAE,gBAAgB,QAY1D;IAED,kDAIC;IAED;;;;;OAKG;IACH,
|
|
1
|
+
{"version":3,"file":"unitView.d.ts","sourceRoot":"","sources":["../../../src/view/unitView.js"],"names":[],"mappings":"AAqBA;;;;GAIG;AACH;QAHkB,MAAM,GAAE,cAAc,kBAAkB,EAAE,OAAO;EASjE;AAEF;IACI;;;;;;;OAOG;IACH;;;;;;;;OAQG;IACH,kBAPW,OAAO,iBAAiB,EAAE,QAAQ,WAClC,OAAO,yBAAyB,EAAE,OAAO,gBACzC,OAAO,oBAAoB,EAAE,OAAO,cACpC,OAAO,WAAW,EAAE,OAAO,QAC3B,MAAM,YACN,OAAO,WAAW,EAAE,WAAW,EAkBzC;IAbG,yCAAgB;IAIZ,iDAAiD;IACjD,MADW,OAAO,kBAAkB,EAAE,OAAO,CACnB;IAUlC;;;;OAIG;IACH,gBAJW,OAAO,4CAA4C,EAAE,OAAO,UAC5D,OAAO,uBAAuB,EAAE,OAAO,YACvC,OAAO,uBAAuB,EAAE,gBAAgB,QAY1D;IAED,kDAIC;IAED;;;;;OAKG;IACH,iEAgFC;IAED;;;OAGG;IACH,mGASC;IAkBD;;OAEG;IACH,uDAEC;IAED;;OAEG;IACH,6/CAcC;IAED;;;;;OAKG;IACH,6BAHW,OAAO,oBAAoB,EAAE,gBAAgB,iDAkBvD;IAED;;;;;;;;;;;;OAYG;IACH,gHA2CC;IAED,uBAQC;IAgBD;;;;OAIG;IACH,8BAJW,MAAM,+DAEJ,OAAO,iBAAiB,EAAE,kBAAkB,CAKxD;CACJ;0BA1VyB,oBAAoB"}
|
|
@@ -160,9 +160,16 @@ export default class UnitView extends ContainerView {
|
|
|
160
160
|
);
|
|
161
161
|
} else if (type == "scale" && isChannelWithScale(channel)) {
|
|
162
162
|
if (!view.resolutions[type][targetChannel]) {
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
163
|
+
const resolution = new ScaleResolution(targetChannel);
|
|
164
|
+
view.resolutions[type][targetChannel] = resolution;
|
|
165
|
+
|
|
166
|
+
resolution.addEventListener("range", (event) => {
|
|
167
|
+
// Create if WebGLHelper is available, i.e., if not running in headless mode
|
|
168
|
+
this.context.glHelper?.createRangeTexture(
|
|
169
|
+
event.scaleResolution,
|
|
170
|
+
true
|
|
171
|
+
);
|
|
172
|
+
});
|
|
166
173
|
}
|
|
167
174
|
view.resolutions[type][targetChannel].pushUnitView(
|
|
168
175
|
this,
|
package/dist/src/view/view.js
CHANGED
|
@@ -217,7 +217,7 @@ export default class View {
|
|
|
217
217
|
|
|
218
218
|
const scale = this.getScaleResolution(
|
|
219
219
|
dimension == "width" ? "x" : "y"
|
|
220
|
-
)?.
|
|
220
|
+
)?.scale;
|
|
221
221
|
|
|
222
222
|
if (scale) {
|
|
223
223
|
// Note: this and all ancestral views need to be refreshed when the domain is changed.
|
|
@@ -706,7 +706,7 @@ function createViewOpacityFunction(view) {
|
|
|
706
706
|
} else if (isDynamicOpacity(opacityDef)) {
|
|
707
707
|
/** @type {(channel: import("../spec/channel.js").ChannelWithScale) => any} */
|
|
708
708
|
const getScale = (channel) => {
|
|
709
|
-
const scale = view.getScaleResolution(channel)?.
|
|
709
|
+
const scale = view.getScaleResolution(channel)?.scale;
|
|
710
710
|
// Only works on linear scales
|
|
711
711
|
if (["linear", "index", "locus"].includes(scale?.type)) {
|
|
712
712
|
return scale;
|