@genome-spy/core 0.72.0 → 0.73.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.
Files changed (103) hide show
  1. package/LICENSE +1 -1
  2. package/dist/bundle/index.es.js +6779 -5393
  3. package/dist/bundle/index.js +133 -121
  4. package/dist/schema.json +281 -17
  5. package/dist/src/data/formats/bed.d.ts +8 -0
  6. package/dist/src/data/formats/bed.d.ts.map +1 -0
  7. package/dist/src/data/formats/bed.js +53 -0
  8. package/dist/src/data/formats/bedpe.d.ts +8 -0
  9. package/dist/src/data/formats/bedpe.d.ts.map +1 -0
  10. package/dist/src/data/formats/bedpe.js +160 -0
  11. package/dist/src/data/sources/dataUtils.d.ts +16 -0
  12. package/dist/src/data/sources/dataUtils.d.ts.map +1 -1
  13. package/dist/src/data/sources/dataUtils.js +53 -3
  14. package/dist/src/data/sources/urlSource.d.ts +4 -0
  15. package/dist/src/data/sources/urlSource.d.ts.map +1 -1
  16. package/dist/src/data/sources/urlSource.js +133 -14
  17. package/dist/src/genome/assemblyPreflight.d.ts +31 -0
  18. package/dist/src/genome/assemblyPreflight.d.ts.map +1 -0
  19. package/dist/src/genome/assemblyPreflight.js +99 -0
  20. package/dist/src/genome/genome.d.ts +2 -2
  21. package/dist/src/genome/genome.d.ts.map +1 -1
  22. package/dist/src/genome/genome.js +4 -0
  23. package/dist/src/genome/genomeStore.d.ts +34 -3
  24. package/dist/src/genome/genomeStore.d.ts.map +1 -1
  25. package/dist/src/genome/genomeStore.js +409 -18
  26. package/dist/src/genome/rootGenomeConfig.d.ts +26 -0
  27. package/dist/src/genome/rootGenomeConfig.d.ts.map +1 -0
  28. package/dist/src/genome/rootGenomeConfig.js +94 -0
  29. package/dist/src/genomeSpy/interactionController.d.ts +5 -1
  30. package/dist/src/genomeSpy/interactionController.d.ts.map +1 -1
  31. package/dist/src/genomeSpy/interactionController.js +244 -29
  32. package/dist/src/genomeSpy/renderCoordinator.js +1 -1
  33. package/dist/src/genomeSpy.d.ts +13 -3
  34. package/dist/src/genomeSpy.d.ts.map +1 -1
  35. package/dist/src/genomeSpy.js +81 -7
  36. package/dist/src/gl/canvasSizeHelper.d.ts +74 -0
  37. package/dist/src/gl/canvasSizeHelper.d.ts.map +1 -0
  38. package/dist/src/gl/canvasSizeHelper.js +203 -0
  39. package/dist/src/gl/webGLHelper.d.ts +25 -11
  40. package/dist/src/gl/webGLHelper.d.ts.map +1 -1
  41. package/dist/src/gl/webGLHelper.js +59 -33
  42. package/dist/src/index.d.ts.map +1 -1
  43. package/dist/src/index.js +5 -2
  44. package/dist/src/marks/link.d.ts.map +1 -1
  45. package/dist/src/marks/link.js +5 -3
  46. package/dist/src/marks/mark.d.ts.map +1 -1
  47. package/dist/src/marks/mark.js +6 -1
  48. package/dist/src/scales/domainPlanner.d.ts +34 -3
  49. package/dist/src/scales/domainPlanner.d.ts.map +1 -1
  50. package/dist/src/scales/domainPlanner.js +247 -26
  51. package/dist/src/scales/scaleInstanceManager.d.ts +2 -1
  52. package/dist/src/scales/scaleInstanceManager.d.ts.map +1 -1
  53. package/dist/src/scales/scaleInstanceManager.js +10 -11
  54. package/dist/src/scales/scaleInteractionController.d.ts.map +1 -1
  55. package/dist/src/scales/scaleInteractionController.js +16 -14
  56. package/dist/src/scales/scaleResolution.d.ts +16 -0
  57. package/dist/src/scales/scaleResolution.d.ts.map +1 -1
  58. package/dist/src/scales/scaleResolution.js +314 -54
  59. package/dist/src/scales/scaleResolutionTestUtils.d.ts +21 -0
  60. package/dist/src/scales/scaleResolutionTestUtils.d.ts.map +1 -0
  61. package/dist/src/scales/scaleResolutionTestUtils.js +33 -0
  62. package/dist/src/scales/selectionDomainUtils.d.ts +22 -0
  63. package/dist/src/scales/selectionDomainUtils.d.ts.map +1 -0
  64. package/dist/src/scales/selectionDomainUtils.js +79 -0
  65. package/dist/src/scales/zoomDomainUtils.d.ts +18 -0
  66. package/dist/src/scales/zoomDomainUtils.d.ts.map +1 -0
  67. package/dist/src/scales/zoomDomainUtils.js +69 -0
  68. package/dist/src/screenshotHarness.d.ts +16 -0
  69. package/dist/src/screenshotHarness.d.ts.map +1 -0
  70. package/dist/src/screenshotHarness.js +242 -0
  71. package/dist/src/singlePageApp.js +1 -1
  72. package/dist/src/spec/data.d.ts +23 -3
  73. package/dist/src/spec/genome.d.ts +22 -2
  74. package/dist/src/spec/parameter.d.ts +39 -2
  75. package/dist/src/spec/root.d.ts +20 -1
  76. package/dist/src/spec/scale.d.ts +41 -5
  77. package/dist/src/styles/genome-spy.css +8 -0
  78. package/dist/src/styles/genome-spy.css.d.ts +1 -1
  79. package/dist/src/styles/genome-spy.css.d.ts.map +1 -1
  80. package/dist/src/styles/genome-spy.css.js +8 -0
  81. package/dist/src/tooltip/dataTooltipHandler.js +59 -10
  82. package/dist/src/types/embedApi.d.ts +19 -0
  83. package/dist/src/utils/inferSpecBaseUrl.d.ts +14 -0
  84. package/dist/src/utils/inferSpecBaseUrl.d.ts.map +1 -0
  85. package/dist/src/utils/inferSpecBaseUrl.js +73 -0
  86. package/dist/src/utils/interactionEvent.d.ts +53 -3
  87. package/dist/src/utils/interactionEvent.d.ts.map +1 -1
  88. package/dist/src/utils/interactionEvent.js +62 -1
  89. package/dist/src/view/containerMutationHelper.d.ts.map +1 -1
  90. package/dist/src/view/containerMutationHelper.js +8 -0
  91. package/dist/src/view/dataReadiness.d.ts +2 -2
  92. package/dist/src/view/dataReadiness.d.ts.map +1 -1
  93. package/dist/src/view/dataReadiness.js +63 -58
  94. package/dist/src/view/facetView.js +1 -1
  95. package/dist/src/view/gridView/gridChild.d.ts +7 -0
  96. package/dist/src/view/gridView/gridChild.d.ts.map +1 -1
  97. package/dist/src/view/gridView/gridChild.js +180 -11
  98. package/dist/src/view/gridView/gridView.d.ts.map +1 -1
  99. package/dist/src/view/gridView/gridView.js +60 -17
  100. package/dist/src/view/zoom.d.ts +14 -2
  101. package/dist/src/view/zoom.d.ts.map +1 -1
  102. package/dist/src/view/zoom.js +373 -76
  103. package/package.json +4 -2
@@ -2,14 +2,22 @@ import { span } from "vega-util";
2
2
  import { isContinuous } from "vega-scale";
3
3
 
4
4
  import { LOCUS } from "./scaleResolutionConstants.js";
5
+ import { requireIntervalSelection } from "./selectionDomainUtils.js";
5
6
  import createDomain from "../utils/domainArray.js";
6
7
  import { getAccessorDomainKey, isScaleAccessor } from "../encoder/accessor.js";
8
+ import { getPrimaryChannel } from "../encoder/encoder.js";
7
9
 
8
10
  /**
9
11
  * @typedef {import("../utils/domainArray.js").DomainArray} DomainArray
10
12
  * @typedef {import("../spec/scale.js").ComplexDomain} ComplexDomain
11
13
  * @typedef {import("../spec/scale.js").ScalarDomain} ScalarDomain
14
+ * @typedef {import("../spec/scale.js").SelectionDomainRef} SelectionDomainRef
12
15
  * @typedef {import("./scaleResolution.js").ScaleResolutionMember} ScaleResolutionMember
16
+ * @typedef {{
17
+ * param: string,
18
+ * encoding: "x" | "y",
19
+ * sync: "auto" | "oneWay" | "twoWay",
20
+ * }} SelectionDomainLinkInfo
13
21
  */
14
22
 
15
23
  export default class DomainPlanner {
@@ -22,7 +30,7 @@ export default class DomainPlanner {
22
30
  /** @type {() => import("../spec/channel.js").Type} */
23
31
  #getType;
24
32
 
25
- /** @type {() => number[]} */
33
+ /** @type {(assembly: import("../spec/scale.js").Scale["assembly"] | undefined) => number[]} */
26
34
  #getLocusExtent;
27
35
 
28
36
  /** @type {(interval: ScalarDomain | ComplexDomain) => number[]} */
@@ -34,6 +42,9 @@ export default class DomainPlanner {
34
42
  /** @type {DomainArray | undefined} */
35
43
  #configuredDomain;
36
44
 
45
+ /** @type {SelectionDomainLinkInfo | undefined} */
46
+ #selectionDomainLinkInfo = undefined;
47
+
37
48
  #configuredDomainDirty = true;
38
49
 
39
50
  /** @type {WeakMap<ScaleResolutionMember, import("../types/encoder.js").ScaleAccessor[]>} */
@@ -44,7 +55,7 @@ export default class DomainPlanner {
44
55
  * @param {() => Set<ScaleResolutionMember>} options.getMembers
45
56
  * @param {() => Set<ScaleResolutionMember>} [options.getDataMembers]
46
57
  * @param {() => import("../spec/channel.js").Type} options.getType
47
- * @param {() => number[]} options.getLocusExtent
58
+ * @param {(assembly: import("../spec/scale.js").Scale["assembly"] | undefined) => number[]} options.getLocusExtent
48
59
  * @param {(interval: ScalarDomain | ComplexDomain) => number[]} options.fromComplexInterval
49
60
  */
50
61
  constructor({
@@ -72,25 +83,51 @@ export default class DomainPlanner {
72
83
  return !!this.getConfiguredDomain();
73
84
  }
74
85
 
86
+ hasSelectionConfiguredDomain() {
87
+ this.getConfiguredDomain();
88
+ return !!this.#selectionDomainLinkInfo;
89
+ }
90
+
91
+ /**
92
+ * @returns {SelectionDomainLinkInfo | undefined}
93
+ */
94
+ getSelectionConfiguredDomainInfo() {
95
+ this.getConfiguredDomain();
96
+ return this.#selectionDomainLinkInfo;
97
+ }
98
+
75
99
  invalidateConfiguredDomain() {
76
100
  this.#configuredDomainDirty = true;
77
101
  }
78
102
 
103
+ /**
104
+ * Returns the default domain without considering configured domains.
105
+ *
106
+ * @param {boolean} [extractDataDomain]
107
+ * @param {import("../spec/scale.js").Scale["assembly"]} [locusAssembly]
108
+ * @returns {any[]}
109
+ */
110
+ getDefaultDomain(extractDataDomain = false, locusAssembly) {
111
+ return resolveDefaultDomain(
112
+ this.#getType(),
113
+ this.#getLocusExtent,
114
+ extractDataDomain ? this.getDataDomain() : undefined,
115
+ locusAssembly
116
+ );
117
+ }
118
+
79
119
  /**
80
120
  * Returns the configured domain or a data-derived/default domain.
81
121
  *
82
122
  * @param {boolean} [extractDataDomain]
123
+ * @param {import("../spec/scale.js").Scale["assembly"]} [locusAssembly]
83
124
  * @returns {any[]}
84
125
  */
85
- getConfiguredOrDefaultDomain(extractDataDomain = false) {
126
+ getConfiguredOrDefaultDomain(extractDataDomain = false, locusAssembly) {
86
127
  // TODO: intersect the domain with zoom extent (if it's defined)
87
128
  return (
88
129
  this.getConfiguredDomain() ??
89
- resolveDefaultDomain(
90
- this.#getType(),
91
- this.#getLocusExtent,
92
- extractDataDomain ? this.getDataDomain() : undefined
93
- )
130
+ this.getDefaultDomain(extractDataDomain, locusAssembly)
94
131
  );
95
132
  }
96
133
 
@@ -104,13 +141,14 @@ export default class DomainPlanner {
104
141
  return this.#configuredDomain;
105
142
  }
106
143
 
107
- const domain = resolveConfiguredDomain(
144
+ const configuredDomain = resolveConfiguredDomain(
108
145
  this.#getMembers(),
109
146
  this.#fromComplexInterval
110
147
  );
111
- this.#configuredDomain = domain;
148
+ this.#configuredDomain = configuredDomain.domain;
149
+ this.#selectionDomainLinkInfo = configuredDomain.selectionRef;
112
150
  this.#configuredDomainDirty = false;
113
- return domain;
151
+ return configuredDomain.domain;
114
152
  }
115
153
 
116
154
  /**
@@ -184,25 +222,207 @@ export default class DomainPlanner {
184
222
  /**
185
223
  * @param {Set<ScaleResolutionMember>} members
186
224
  * @param {(interval: ScalarDomain | ComplexDomain) => number[]} fromComplexInterval
187
- * @returns {DomainArray | undefined}
225
+ * @returns {{
226
+ * domain: DomainArray | undefined,
227
+ * selectionRef: SelectionDomainLinkInfo | undefined,
228
+ * }}
188
229
  */
189
230
  function resolveConfiguredDomain(members, fromComplexInterval) {
190
- const domains = Array.from(members)
231
+ const domainMembers = Array.from(members)
191
232
  .filter((member) => member.contributesToDomain)
192
- .map((member) => member.channelDef)
193
- .filter((channelDef) => channelDef.scale?.domain)
194
- .map((channelDef) =>
195
- // TODO: Handle ExprRefs and Param in domain
196
- createDomain(
197
- channelDef.type,
198
- // Chrom/pos must be linearized first
199
- fromComplexInterval(channelDef.scale.domain)
200
- )
233
+ .filter((member) => member.channelDef.scale?.domain);
234
+
235
+ /** @type {DomainArray[]} */
236
+ const domains = [];
237
+
238
+ /** @type {string | undefined} */
239
+ let selectionRefKey = undefined;
240
+ /** @type {string | undefined} */
241
+ let selectionRefDescription = undefined;
242
+ /** @type {"auto" | "oneWay" | "twoWay" | undefined} */
243
+ let selectionRefSync = undefined;
244
+ /** @type {SelectionDomainLinkInfo | undefined} */
245
+ let selectionRef = undefined;
246
+ let hasLiteralDomain = false;
247
+
248
+ for (const member of domainMembers) {
249
+ const domainDef = member.channelDef.scale.domain;
250
+ if (isSelectionDomainRef(domainDef)) {
251
+ if (hasLiteralDomain) {
252
+ throw new Error(
253
+ "Cannot mix selection-driven and literal configured domains on a shared scale."
254
+ );
255
+ }
256
+
257
+ const resolved = resolveSelectionDomain(
258
+ member,
259
+ domainDef,
260
+ fromComplexInterval
261
+ );
262
+
263
+ if (selectionRefKey && selectionRefKey !== resolved.key) {
264
+ throw new Error(
265
+ "Conflicting selection domain references on a shared scale: " +
266
+ selectionRefDescription +
267
+ " vs " +
268
+ resolved.description +
269
+ "."
270
+ );
271
+ }
272
+
273
+ if (!selectionRefSync) {
274
+ selectionRefSync = resolved.sync;
275
+ } else if (selectionRefSync === "auto") {
276
+ selectionRefSync = resolved.sync;
277
+ } else if (resolved.sync !== "auto") {
278
+ if (selectionRefSync !== resolved.sync) {
279
+ throw new Error(
280
+ "Conflicting selection domain sync modes on a shared scale: " +
281
+ selectionRefSync +
282
+ " vs " +
283
+ resolved.sync +
284
+ "."
285
+ );
286
+ }
287
+ }
288
+
289
+ selectionRefKey = resolved.key;
290
+ selectionRefDescription = resolved.description;
291
+ selectionRef = {
292
+ param: resolved.param,
293
+ encoding: resolved.encoding,
294
+ sync: selectionRefSync,
295
+ };
296
+
297
+ if (resolved.domain) {
298
+ domains.push(resolved.domain);
299
+ }
300
+ continue;
301
+ }
302
+
303
+ if (selectionRefKey) {
304
+ throw new Error(
305
+ "Cannot mix literal configured domains with selection-driven domains on a shared scale."
306
+ );
307
+ }
308
+
309
+ hasLiteralDomain = true;
310
+ domains.push(
311
+ createDomain(member.channelDef.type, fromComplexInterval(domainDef))
201
312
  );
313
+ }
202
314
 
203
315
  if (domains.length > 0) {
204
- return domains.reduce((acc, curr) => acc.extendAll(curr));
316
+ return {
317
+ domain: domains.reduce((acc, curr) => acc.extendAll(curr)),
318
+ selectionRef,
319
+ };
320
+ }
321
+
322
+ if (selectionRefKey) {
323
+ // Selection refs are still the source of truth even when the
324
+ // selection interval currently resolves to no domain.
325
+ return { domain: undefined, selectionRef };
326
+ }
327
+
328
+ return { domain: undefined, selectionRef: undefined };
329
+ }
330
+
331
+ /**
332
+ * @param {ScaleResolutionMember} member
333
+ * @param {SelectionDomainRef} domainRef
334
+ * @param {(interval: ScalarDomain | ComplexDomain) => number[]} fromComplexInterval
335
+ * @returns {{
336
+ * domain: DomainArray | undefined,
337
+ * key: string,
338
+ * description: string,
339
+ * param: string,
340
+ * encoding: "x" | "y",
341
+ * sync: "auto" | "oneWay" | "twoWay",
342
+ * }}
343
+ */
344
+ function resolveSelectionDomain(member, domainRef, fromComplexInterval) {
345
+ const paramName = domainRef.param;
346
+ const syncMode = domainRef.sync ?? "auto";
347
+
348
+ if (syncMode !== "auto" && syncMode !== "oneWay" && syncMode !== "twoWay") {
349
+ throw new Error(
350
+ `Invalid selection domain sync mode "${syncMode}" for parameter "${paramName}".`
351
+ );
352
+ }
353
+
354
+ const resolvedChannel = resolveSelectionDomainChannel(
355
+ member.channel,
356
+ domainRef,
357
+ paramName
358
+ );
359
+
360
+ const paramRuntime = member.view.paramRuntime;
361
+ const selection = requireIntervalSelection(
362
+ paramRuntime?.findValue(paramName),
363
+ paramName
364
+ );
365
+
366
+ const interval = selection.intervals[resolvedChannel];
367
+ const key = [paramName, resolvedChannel].join("|");
368
+ const description = paramName + "." + resolvedChannel;
369
+
370
+ if (!interval || interval.length !== 2) {
371
+ return {
372
+ domain: undefined,
373
+ key,
374
+ description,
375
+ param: paramName,
376
+ encoding: resolvedChannel,
377
+ sync: syncMode,
378
+ };
379
+ }
380
+
381
+ return {
382
+ domain: createDomain(
383
+ member.channelDef.type,
384
+ fromComplexInterval(interval)
385
+ ),
386
+ key,
387
+ description,
388
+ param: paramName,
389
+ encoding: resolvedChannel,
390
+ sync: syncMode,
391
+ };
392
+ }
393
+
394
+ /**
395
+ * @param {import("../spec/channel.js").ChannelWithScale} channel
396
+ * @param {SelectionDomainRef} domainRef
397
+ * @param {string} paramName
398
+ * @returns {"x" | "y"}
399
+ */
400
+ function resolveSelectionDomainChannel(channel, domainRef, paramName) {
401
+ if (domainRef.encoding) {
402
+ return domainRef.encoding;
205
403
  }
404
+
405
+ const primaryChannel = getPrimaryChannel(channel);
406
+ if (primaryChannel === "x" || primaryChannel === "y") {
407
+ return primaryChannel;
408
+ }
409
+
410
+ throw new Error(
411
+ `Selection domain reference "${paramName}" on channel "${channel}" requires an explicit "encoding" ("x" or "y").`
412
+ );
413
+ }
414
+
415
+ /**
416
+ * @param {any} domain
417
+ * @returns {domain is SelectionDomainRef}
418
+ */
419
+ export function isSelectionDomainRef(domain) {
420
+ return (
421
+ typeof domain === "object" &&
422
+ domain !== null &&
423
+ !Array.isArray(domain) &&
424
+ typeof domain.param === "string"
425
+ );
206
426
  }
207
427
 
208
428
  /**
@@ -273,13 +493,14 @@ function resolveDataDomain(members, getType, getAccessorsForMember) {
273
493
 
274
494
  /**
275
495
  * @param {import("../spec/channel.js").Type} type
276
- * @param {() => number[]} getLocusExtent
496
+ * @param {(assembly: import("../spec/scale.js").Scale["assembly"] | undefined) => number[]} getLocusExtent
277
497
  * @param {DomainArray | undefined} dataDomain
498
+ * @param {import("../spec/scale.js").Scale["assembly"] | undefined} locusAssembly
278
499
  * @returns {any[]}
279
500
  */
280
- function resolveDefaultDomain(type, getLocusExtent, dataDomain) {
501
+ function resolveDefaultDomain(type, getLocusExtent, dataDomain, locusAssembly) {
281
502
  if (type == LOCUS) {
282
- return getLocusExtent();
503
+ return getLocusExtent(locusAssembly);
283
504
  }
284
505
  return dataDomain ?? [];
285
506
  }
@@ -18,9 +18,10 @@ export default class ScaleInstanceManager {
18
18
  props: import("../spec/scale.js").Scale;
19
19
  };
20
20
  /**
21
+ * @param {import("../spec/scale.js").Scale["assembly"]} [assembly]
21
22
  * @returns {import("../genome/genome.js").default}
22
23
  */
23
- getLocusGenome(): import("../genome/genome.js").default;
24
+ getLocusGenome(assembly?: import("../spec/scale.js").Scale["assembly"]): import("../genome/genome.js").default;
24
25
  /**
25
26
  * @param {import("../spec/scale.js").Scale} props
26
27
  * @returns {ScaleWithProps}
@@ -1 +1 @@
1
- {"version":3,"file":"scaleInstanceManager.d.ts","sourceRoot":"","sources":["../../../src/scales/scaleInstanceManager.js"],"names":[],"mappings":"AAMA;IA0BI;;;;;;OAMG;IACH,iFALG;QAAkH,eAAe,EAAzH,MAAM;YAAE,gBAAgB,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,0BAA0B,EAAE,eAAe,CAAA;SAAE;QAC5E,aAAa,EAAjC,MAAM,IAAI;QACW,cAAc,GAAnC,MAAM,IAAI;QAC6D,cAAc,GAArF,MAAM,OAAO,0BAA0B,EAAE,OAAO,GAAG,SAAS;KAA0B,EAYhG;IAED;eA1CkC,OAAO,kBAAkB,EAAE,KAAK;MA4CjE;IAED;;OAEG;IACH,kBAFa,OAAO,qBAAqB,EAAE,OAAO,CASjD;IAED;;;OAGG;IACH,mBAHW,OAAO,kBAAkB,EAAE,KAAK;eA3DT,OAAO,kBAAkB,EAAE,KAAK;MAgFjE;IAoBD;;OAEG;IACH,wBAFW,OAAO,kBAAkB,EAAE,KAAK,QAc1C;IAED;;;OAGG;IACH,4CAHW,MAAM,IAAI,GACR,IAAI,CAShB;IAmFD,gBAGC;;CACJ"}
1
+ {"version":3,"file":"scaleInstanceManager.d.ts","sourceRoot":"","sources":["../../../src/scales/scaleInstanceManager.js"],"names":[],"mappings":"AAMA;IA0BI;;;;;;OAMG;IACH,iFALG;QAAkH,eAAe,EAAzH,MAAM;YAAE,gBAAgB,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,0BAA0B,EAAE,eAAe,CAAA;SAAE;QAC5E,aAAa,EAAjC,MAAM,IAAI;QACW,cAAc,GAAnC,MAAM,IAAI;QAC6D,cAAc,GAArF,MAAM,OAAO,0BAA0B,EAAE,OAAO,GAAG,SAAS;KAA0B,EAYhG;IAED;eA1CkC,OAAO,kBAAkB,EAAE,KAAK;MA4CjE;IAED;;;OAGG;IACH,0BAHW,OAAO,kBAAkB,EAAE,KAAK,CAAC,UAAU,CAAC,GAC1C,OAAO,qBAAqB,EAAE,OAAO,CAajD;IAED;;;OAGG;IACH,mBAHW,OAAO,kBAAkB,EAAE,KAAK;eAhET,OAAO,kBAAkB,EAAE,KAAK;MAqFjE;IAcD;;OAEG;IACH,wBAFW,OAAO,kBAAkB,EAAE,KAAK,QAc1C;IAED;;;OAGG;IACH,4CAHW,MAAM,IAAI,GACR,IAAI,CAShB;IAmFD,gBAGC;;CACJ"}
@@ -54,15 +54,20 @@ export default class ScaleInstanceManager {
54
54
  }
55
55
 
56
56
  /**
57
+ * @param {import("../spec/scale.js").Scale["assembly"]} [assembly]
57
58
  * @returns {import("../genome/genome.js").default}
58
59
  */
59
- getLocusGenome() {
60
+ getLocusGenome(assembly) {
60
61
  const genomeStore = this.#getGenomeStore?.();
61
- const genome = genomeStore?.getGenome();
62
- if (!genome) {
62
+ if (!genomeStore) {
63
63
  throw new Error("No genome has been defined!");
64
64
  }
65
- return genome;
65
+
66
+ if (assembly) {
67
+ return genomeStore.getGenome(assembly);
68
+ }
69
+
70
+ return genomeStore.getGenome();
66
71
  }
67
72
 
68
73
  /**
@@ -98,13 +103,7 @@ export default class ScaleInstanceManager {
98
103
  return;
99
104
  }
100
105
 
101
- const genomeStore = this.#getGenomeStore?.();
102
- const genome = genomeStore?.getGenome(props.assembly);
103
- if (!genome) {
104
- throw new Error("No genome has been defined!");
105
- }
106
-
107
- scale.genome(genome);
106
+ scale.genome(this.getLocusGenome(props.assembly));
108
107
  }
109
108
 
110
109
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"scaleInteractionController.d.ts","sourceRoot":"","sources":["../../../src/scales/scaleInteractionController.js"],"names":[],"mappings":"AAoBA;;;;;;;GAOG;AAEH;IAsBI;;;;;;;;OAQG;IACH,wHAPG;QAAsC,QAAQ,EAAtC,MAAM,cAAc;QACkC,WAAW,EAAjE,MAAM,OAAO,sBAAsB,EAAE,OAAO;QACpB,wBAAwB,EAAhD,MAAM,MAAM,EAAE;QACU,cAAc,EAAtC,MAAM,MAAM,EAAE;QAC8C,mBAAmB,EAA/E,CAAC,MAAM,EAAE,YAAY,GAAG,aAAa,KAAK,MAAM,EAAE;QAC1B,eAAe,EAAvC,MAAM,MAAM,EAAE;KACxB,EAeA;IAED,0BAUC;IAED,sBAEC;IAED,8BAGC;IAED;;;;OAIG;IACH,sCAJW,MAAM,EAAE,aACR,MAAM,EAAE,GACN,SAAS,GAAG,SAAS,GAAG,QAAQ,GAAG,MAAM,CAarD;IAED;;;;OAIG;IACH,oBAQC;IAED;;;;;;;OAOG;IACH,kBALW,MAAM,eACN,MAAM,OACN,MAAM,GACJ,OAAO,CA2BnB;IAED;;;;;;OAMG;IACH,eAJW,aAAa,GAAG,aAAa,aAC7B,OAAO,GAAG,MAAM,iBA+D1B;IASD;;;;OAIG;IACH,qBAcC;IAED;;OAEG;IACH,uBAOC;;CACJ;4BA5PY,OAAO,kBAAkB,EAAE,aAAa;2BACxC,OAAO,kBAAkB,EAAE,YAAY;4BACvC,OAAO,kBAAkB,EAAE,aAAa;yBACxC,OAAO,kBAAkB,EAAE,UAAU;wBACrC,OAAO,qBAAqB,EAAE,SAAS;6BACvC,SAAS,GAAG;IAAE,KAAK,EAAE,OAAO,kBAAkB,EAAE,KAAK,CAAA;CAAE"}
1
+ {"version":3,"file":"scaleInteractionController.d.ts","sourceRoot":"","sources":["../../../src/scales/scaleInteractionController.js"],"names":[],"mappings":"AAkBA;;;;;;;GAOG;AAEH;IAsBI;;;;;;;;OAQG;IACH,wHAPG;QAAsC,QAAQ,EAAtC,MAAM,cAAc;QACkC,WAAW,EAAjE,MAAM,OAAO,sBAAsB,EAAE,OAAO;QACpB,wBAAwB,EAAhD,MAAM,MAAM,EAAE;QACU,cAAc,EAAtC,MAAM,MAAM,EAAE;QAC8C,mBAAmB,EAA/E,CAAC,MAAM,EAAE,YAAY,GAAG,aAAa,KAAK,MAAM,EAAE;QAC1B,eAAe,EAAvC,MAAM,MAAM,EAAE;KACxB,EAeA;IAED,0BAUC;IAED,sBAEC;IAED,8BAGC;IAED;;;;OAIG;IACH,sCAJW,MAAM,EAAE,aACR,MAAM,EAAE,GACN,SAAS,GAAG,SAAS,GAAG,QAAQ,GAAG,MAAM,CAarD;IAED;;;;OAIG;IACH,oBAQC;IAED;;;;;;;OAOG;IACH,kBALW,MAAM,eACN,MAAM,OACN,MAAM,GACJ,OAAO,CA2BnB;IAED;;;;;;OAMG;IACH,eAJW,aAAa,GAAG,aAAa,aAC7B,OAAO,GAAG,MAAM,iBA+D1B;IASD;;;;OAIG;IACH,qBAcC;IAED;;OAEG;IACH,uBAOC;;CACJ;4BA5PY,OAAO,kBAAkB,EAAE,aAAa;2BACxC,OAAO,kBAAkB,EAAE,YAAY;4BACvC,OAAO,kBAAkB,EAAE,aAAa;yBACxC,OAAO,kBAAkB,EAAE,UAAU;wBACrC,OAAO,qBAAqB,EAAE,SAAS;6BACvC,SAAS,GAAG;IAAE,KAAK,EAAE,OAAO,kBAAkB,EAAE,KAAK,CAAA;CAAE"}
@@ -7,9 +7,6 @@ import {
7
7
  panLog,
8
8
  panPow,
9
9
  span,
10
- zoomLinear,
11
- zoomLog,
12
- zoomPow,
13
10
  } from "vega-util";
14
11
  import { isContinuous, isDiscrete } from "vega-scale";
15
12
  import { easeCubicInOut } from "d3-ease";
@@ -17,6 +14,7 @@ import { easeCubicInOut } from "d3-ease";
17
14
  import eerp from "../utils/eerp.js";
18
15
  import { shallowArrayEquals } from "../utils/arrayUtils.js";
19
16
  import { createCancelToken } from "../utils/transition.js";
17
+ import { zoomDomainByScaleType } from "./zoomDomainUtils.js";
20
18
 
21
19
  /**
22
20
  * @typedef {import("../spec/scale.js").NumericDomain} NumericDomain
@@ -121,7 +119,7 @@ export default class ScaleInteractionController {
121
119
  isZoomed() {
122
120
  return (
123
121
  this.isZoomingSupported() &&
124
- shallowArrayEquals(
122
+ !shallowArrayEquals(
125
123
  this.#getResetDomain(),
126
124
  this.#getScale().domain()
127
125
  )
@@ -326,17 +324,14 @@ function applyZoomTransform(scale, domain, scaleFactor, scaleAnchor, pan) {
326
324
  anchor += scale.align();
327
325
  }
328
326
 
329
- // TODO: symlog
330
327
  switch (scale.type) {
331
328
  case "linear":
332
329
  case "index":
333
330
  case "locus":
334
331
  newDomain = panLinear(newDomain, pan || 0);
335
- newDomain = zoomLinear(newDomain, anchor, scaleFactor);
336
332
  break;
337
333
  case "log":
338
334
  newDomain = panLog(newDomain, pan || 0);
339
- newDomain = zoomLog(newDomain, anchor, scaleFactor);
340
335
  break;
341
336
  case "pow":
342
337
  case "sqrt": {
@@ -345,19 +340,26 @@ function applyZoomTransform(scale, domain, scaleFactor, scaleAnchor, pan) {
345
340
  scale
346
341
  );
347
342
  newDomain = panPow(newDomain, pan || 0, powScale.exponent());
348
- newDomain = zoomPow(
349
- newDomain,
350
- anchor,
351
- scaleFactor,
352
- powScale.exponent()
353
- );
343
+ break;
344
+ }
345
+ case "symlog": {
346
+ if (pan !== 0) {
347
+ throw new Error(
348
+ "Panning is not implemented for: " + scale.type
349
+ );
350
+ }
354
351
  break;
355
352
  }
356
353
  default:
357
354
  throw new Error("Zooming is not implemented for: " + scale.type);
358
355
  }
359
356
 
360
- return newDomain;
357
+ return zoomDomainByScaleType(
358
+ scale,
359
+ /** @type {[number, number]} */ (newDomain),
360
+ anchor,
361
+ scaleFactor
362
+ );
361
363
  }
362
364
 
363
365
  /**
@@ -63,6 +63,22 @@ export default class ScaleResolution implements ScaleResolutionApi {
63
63
  * @returns {() => void}
64
64
  */
65
65
  registerCollectorSubscriptions(collector: import("../data/collector.js").default, accessors: Iterable<import("../types/encoder.js").ScaleAccessor>): () => void;
66
+ /**
67
+ * Returns locus assembly requirements without initializing the scale.
68
+ *
69
+ * This is intentionally side-effect free: it only inspects merged scale
70
+ * properties from registered members and does not touch default domains or
71
+ * instantiate scale instances.
72
+ *
73
+ * @returns {{
74
+ * assembly: import("../spec/scale.js").Scale["assembly"] | undefined,
75
+ * needsDefaultAssembly: boolean
76
+ * }}
77
+ */
78
+ getAssemblyRequirement(): {
79
+ assembly: import("../spec/scale.js").Scale["assembly"] | undefined;
80
+ needsDefaultAssembly: boolean;
81
+ };
66
82
  /**
67
83
  * Reconfigures the scale: updates domain and other settings.
68
84
  *
@@ -1 +1 @@
1
- {"version":3,"file":"scaleResolution.d.ts","sourceRoot":"","sources":["../../../src/scales/scaleResolution.js"],"names":[],"mappings":"AAuCA;;;;;;;;GAQG;AACH;;;;;;;;;;;;;;;;;;GAkBG;AACH;IA8CI;;OAEG;IACH,2DAkCC;IAjCG,8CAAsB;IACtB,0FAA0F;IAC1F,MADW,OAAO,oBAAoB,EAAE,IAAI,CAC5B;IAEhB,iEAAiE;IACjE,MADW,MAAM,CACI;IAoEzB,2BASC;IAqBD;;;;;;;OAOG;IACH,4KAEC;IAED;;;OAGG;IACH,+KAEC;IA6ED;;;OAGG;IACH,uBAHW,qBAAqB,GACnB,MAAM,OAAO,CAYzB;IAED,gBAIC;IAWD;;;;OAIG;IACH,0CAJW,OAAO,sBAAsB,EAAE,OAAO,aACtC,QAAQ,CAAC,OAAO,qBAAqB,EAAE,aAAa,CAAC,GACnD,MAAM,IAAI,CAkCtB;IA4HD;;;;;OAKG;IACH,oBAUC;IAED;;;;;OAKG;IACH,0BAuBC;IAuGD;;OAEG;IACH;eAlkBkC,OAAO,kBAAkB,EAAE,KAAK;MAykBjE;IAED;;;;;;OAMG;IACH;eAllBkC,OAAO,kBAAkB,EAAE,KAAK;MAolBjE;IAED;;;;OAIG;IACH;eA3lBkC,OAAO,kBAAkB,EAAE,KAAK;MAomBjE;IAED,mBAEC;IAED;;;;OAIG;IACH,+DAEC;IAED;;OAEG;IACH,oBAFa,mFAA6B,CAMzC;IAED;;;;OAIG;IACH,oBAEC;IAED;;OAEG;IACH,sBAGC;IAED;;;;;;;OAOG;IACH,kBALW,MAAM,eACN,MAAM,OACN,MAAM,GACJ,OAAO,CAInB;IAED;;;;;;OAMG;IACH,eAJW,mFAA6B,aAC7B,OAAO,GAAG,MAAM,iBAK1B;IAED;;;;OAIG;IACH,qBAEC;IAED;;;;;OAKG;IACH,uBAEC;IAED;;;;;;;OAOG;IACH,wBAoBC;IAED;;;;;OAKG;IACH,uBAFW,MAAM,yDAUhB;IAED;;OAEG;IACH,iBAFW,MAAM,yDAIhB;IAED;;;OAGG;IACH,qBAHW,MAAM,+CAAmB,GACvB,MAAM,CAIlB;IAED;;;OAGG;IACH,8BAHW,kFAA4B,GAC1B,MAAM,EAAE,CAOpB;;CACJ;kCAxyB+B,CAAC,SAApB,6CAAkB;;;;UAGrB,OAAO,qBAAqB,EAAE,OAAO;aACrC,CAAC;gBACD,OAAO,oBAAoB,EAAE,mBAAmB;yBAChD,OAAO;;sBAvBV,+BAA+B;sBAA/B,+BAA+B;wBAA/B,+BAA+B;wBAA/B,+BAA+B;6BAA/B,+BAA+B"}
1
+ {"version":3,"file":"scaleResolution.d.ts","sourceRoot":"","sources":["../../../src/scales/scaleResolution.js"],"names":[],"mappings":"AAgDA;;;;;;;;GAQG;AACH;;;;;;;;;;;;;;;;;;GAkBG;AACH;IAmDI;;OAEG;IACH,2DAkCC;IAjCG,8CAAsB;IACtB,0FAA0F;IAC1F,MADW,OAAO,oBAAoB,EAAE,IAAI,CAC5B;IAEhB,iEAAiE;IACjE,MADW,MAAM,CACI;IAoEzB,2BASC;IAwBD;;;;;;;OAOG;IACH,4KAEC;IAED;;;OAGG;IACH,+KAEC;IAkLD;;;OAGG;IACH,uBAHW,qBAAqB,GACnB,MAAM,OAAO,CAazB;IAED,gBAKC;IA4CD;;;;OAIG;IACH,0CAJW,OAAO,sBAAsB,EAAE,OAAO,aACtC,QAAQ,CAAC,OAAO,qBAAqB,EAAE,aAAa,CAAC,GACnD,MAAM,IAAI,CAkCtB;IA2CD;;;;;;;;;;;OAWG;IACH,0BALa;QACR,QAAQ,EAAE,OAAO,kBAAkB,EAAE,KAAK,CAAC,UAAU,CAAC,GAAG,SAAS,CAAC;QACnE,oBAAoB,EAAE,OAAO,CAAA;KAC9B,CAeH;IAmFD;;;;;OAKG;IACH,oBAYC;IAED;;;;;OAKG;IACH,0BAyBC;IA0HD;;OAEG;IACH;eApwBkC,OAAO,kBAAkB,EAAE,KAAK;MA2wBjE;IAED;;;;;;OAMG;IACH;eApxBkC,OAAO,kBAAkB,EAAE,KAAK;MAsxBjE;IAED;;;;OAIG;IACH;eA7xBkC,OAAO,kBAAkB,EAAE,KAAK;MAsyBjE;IAED,mBAEC;IAED;;;;OAIG;IACH,+DAEC;IAED;;OAEG;IACH,oBAFa,mFAA6B,CAMzC;IAED;;;;OAIG;IACH,oBAEC;IAED;;OAEG;IACH,sBAGC;IAED;;;;;;;OAOG;IACH,kBALW,MAAM,eACN,MAAM,OACN,MAAM,GACJ,OAAO,CAInB;IAED;;;;;;OAMG;IACH,eAJW,mFAA6B,aAC7B,OAAO,GAAG,MAAM,iBAK1B;IAED;;;;OAIG;IACH,qBAEC;IAED;;;;;OAKG;IACH,uBAEC;IAED;;;;;;;OAOG;IACH,wBAoBC;IAED;;;;;OAKG;IACH,uBAFW,MAAM,yDAUhB;IAED;;OAEG;IACH,iBAFW,MAAM,yDAIhB;IAED;;;OAGG;IACH,qBAHW,MAAM,+CAAmB,GACvB,MAAM,CAIlB;IAED;;;OAGG;IACH,8BAHW,kFAA4B,GAC1B,MAAM,EAAE,CAOpB;;CACJ;kCA1+B+B,CAAC,SAApB,6CAAkB;;;;UAGrB,OAAO,qBAAqB,EAAE,OAAO;aACrC,CAAC;gBACD,OAAO,oBAAoB,EAAE,mBAAmB;yBAChD,OAAO;;sBAhCV,+BAA+B;sBAA/B,+BAA+B;wBAA/B,+BAA+B;wBAA/B,+BAA+B;6BAA/B,+BAA+B"}