@genome-spy/core 0.64.0 → 0.66.1

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 (285) hide show
  1. package/dist/bundle/{index-CCJIjehY.js → AbortablePromiseCache-CcuMrnn7.js} +22 -91
  2. package/dist/bundle/browser-BRemItdO.js +138 -0
  3. package/dist/bundle/index-BatuyGAI.js +271 -0
  4. package/dist/bundle/index-ByuE8dvu.js +332 -0
  5. package/dist/bundle/index-Cq3QFUxX.js +1781 -0
  6. package/dist/bundle/{index-C08YCM2T.js → index-D-w7Mmt9.js} +246 -126
  7. package/dist/bundle/index-D28m8tSW.js +1607 -0
  8. package/dist/bundle/index-D74H8TTz.js +508 -0
  9. package/dist/bundle/index-DbJ0oeYM.js +631 -0
  10. package/dist/bundle/index.es.js +15034 -13842
  11. package/dist/bundle/index.js +223 -237
  12. package/dist/bundle/inflate-GtwLkvSP.js +1048 -0
  13. package/dist/bundle/unzip-NywezaRR.js +1492 -0
  14. package/dist/schema.json +22 -4
  15. package/dist/src/config/scaleDefaults.d.ts +8 -0
  16. package/dist/src/config/scaleDefaults.d.ts.map +1 -0
  17. package/dist/src/config/scaleDefaults.js +45 -0
  18. package/dist/src/data/collector.d.ts +7 -2
  19. package/dist/src/data/collector.d.ts.map +1 -1
  20. package/dist/src/data/collector.js +13 -2
  21. package/dist/src/data/dataFlow.d.ts +20 -42
  22. package/dist/src/data/dataFlow.d.ts.map +1 -1
  23. package/dist/src/data/dataFlow.js +57 -80
  24. package/dist/src/data/flowHandle.d.ts +15 -0
  25. package/dist/src/data/flowHandle.d.ts.map +1 -0
  26. package/dist/src/data/flowHandle.js +13 -0
  27. package/dist/src/data/flowInit.d.ts +85 -0
  28. package/dist/src/data/flowInit.d.ts.map +1 -0
  29. package/dist/src/data/flowInit.js +238 -0
  30. package/dist/src/data/flowInit.test.d.ts +2 -0
  31. package/dist/src/data/flowInit.test.d.ts.map +1 -0
  32. package/dist/src/data/flowOptimizer.d.ts +6 -4
  33. package/dist/src/data/flowOptimizer.d.ts.map +1 -1
  34. package/dist/src/data/flowOptimizer.js +29 -14
  35. package/dist/src/data/sources/lazy/axisTickSource.js +1 -1
  36. package/dist/src/data/sources/lazy/bamSource.js +1 -1
  37. package/dist/src/data/sources/lazy/bigBedSource.js +1 -1
  38. package/dist/src/data/sources/lazy/bigWigSource.js +1 -1
  39. package/dist/src/data/sources/lazy/gff3Source.d.ts +2 -6
  40. package/dist/src/data/sources/lazy/gff3Source.d.ts.map +1 -1
  41. package/dist/src/data/sources/lazy/gff3Source.js +4 -8
  42. package/dist/src/data/sources/lazy/indexedFastaSource.d.ts.map +1 -1
  43. package/dist/src/data/sources/lazy/indexedFastaSource.js +17 -17
  44. package/dist/src/data/sources/lazy/singleAxisLazySource.d.ts +1 -1
  45. package/dist/src/data/sources/lazy/singleAxisLazySource.d.ts.map +1 -1
  46. package/dist/src/data/sources/lazy/singleAxisLazySource.js +10 -3
  47. package/dist/src/data/sources/lazy/singleAxisWindowedSource.d.ts.map +1 -1
  48. package/dist/src/data/sources/lazy/singleAxisWindowedSource.js +5 -1
  49. package/dist/src/data/sources/lazy/tabixSource.js +1 -1
  50. package/dist/src/data/transforms/filterScoredLabels.d.ts +1 -1
  51. package/dist/src/data/transforms/filterScoredLabels.d.ts.map +1 -1
  52. package/dist/src/data/transforms/filterScoredLabels.js +1 -1
  53. package/dist/src/data/transforms/linearizeGenomicCoordinate.d.ts.map +1 -1
  54. package/dist/src/data/transforms/linearizeGenomicCoordinate.js +2 -1
  55. package/dist/src/encoder/encoder.d.ts +1 -1
  56. package/dist/src/encoder/encoder.d.ts.map +1 -1
  57. package/dist/src/encoder/encoder.js +1 -1
  58. package/dist/src/genome/scaleLocus.d.ts +39 -0
  59. package/dist/src/genome/scaleLocus.d.ts.map +1 -1
  60. package/dist/src/genome/scaleLocus.js +76 -0
  61. package/dist/src/genomeSpy/canvasExport.d.ts +19 -0
  62. package/dist/src/genomeSpy/canvasExport.d.ts.map +1 -0
  63. package/dist/src/genomeSpy/canvasExport.js +66 -0
  64. package/dist/src/genomeSpy/containerUi.d.ts +17 -0
  65. package/dist/src/genomeSpy/containerUi.d.ts.map +1 -0
  66. package/dist/src/genomeSpy/containerUi.js +78 -0
  67. package/dist/src/genomeSpy/eventListenerRegistry.d.ts +19 -0
  68. package/dist/src/genomeSpy/eventListenerRegistry.d.ts.map +1 -0
  69. package/dist/src/genomeSpy/eventListenerRegistry.js +38 -0
  70. package/dist/src/genomeSpy/inputBindingManager.d.ts +14 -0
  71. package/dist/src/genomeSpy/inputBindingManager.d.ts.map +1 -0
  72. package/dist/src/genomeSpy/inputBindingManager.js +63 -0
  73. package/dist/src/genomeSpy/interactionController.d.ts +40 -0
  74. package/dist/src/genomeSpy/interactionController.d.ts.map +1 -0
  75. package/dist/src/genomeSpy/interactionController.js +371 -0
  76. package/dist/src/genomeSpy/keyboardListenerManager.d.ts +10 -0
  77. package/dist/src/genomeSpy/keyboardListenerManager.d.ts.map +1 -0
  78. package/dist/src/genomeSpy/keyboardListenerManager.js +31 -0
  79. package/dist/src/genomeSpy/loadingIndicatorManager.d.ts +15 -0
  80. package/dist/src/genomeSpy/loadingIndicatorManager.d.ts.map +1 -0
  81. package/dist/src/genomeSpy/loadingIndicatorManager.js +92 -0
  82. package/dist/src/genomeSpy/renderCoordinator.d.ts +22 -0
  83. package/dist/src/genomeSpy/renderCoordinator.d.ts.map +1 -0
  84. package/dist/src/genomeSpy/renderCoordinator.js +118 -0
  85. package/dist/src/genomeSpy/viewContextFactory.d.ts +18 -0
  86. package/dist/src/genomeSpy/viewContextFactory.d.ts.map +1 -0
  87. package/dist/src/genomeSpy/viewContextFactory.js +79 -0
  88. package/dist/src/genomeSpy/viewDataInit.d.ts +12 -0
  89. package/dist/src/genomeSpy/viewDataInit.d.ts.map +1 -0
  90. package/dist/src/genomeSpy/viewDataInit.js +41 -0
  91. package/dist/src/genomeSpy/viewHierarchyConfig.d.ts +14 -0
  92. package/dist/src/genomeSpy/viewHierarchyConfig.d.ts.map +1 -0
  93. package/dist/src/genomeSpy/viewHierarchyConfig.js +24 -0
  94. package/dist/src/genomeSpy/viewHighlight.d.ts +5 -0
  95. package/dist/src/genomeSpy/viewHighlight.d.ts.map +1 -0
  96. package/dist/src/genomeSpy/viewHighlight.js +30 -0
  97. package/dist/src/genomeSpy.d.ts +17 -72
  98. package/dist/src/genomeSpy.d.ts.map +1 -1
  99. package/dist/src/genomeSpy.js +180 -789
  100. package/dist/src/gl/glslScaleGenerator.d.ts +1 -1
  101. package/dist/src/gl/webGLHelper.d.ts +2 -2
  102. package/dist/src/gl/webGLHelper.d.ts.map +1 -1
  103. package/dist/src/gl/webGLHelper.js +4 -4
  104. package/dist/src/index.d.ts.map +1 -1
  105. package/dist/src/index.js +2 -12
  106. package/dist/src/marks/mark.d.ts +1 -0
  107. package/dist/src/marks/mark.d.ts.map +1 -1
  108. package/dist/src/marks/mark.js +26 -3
  109. package/dist/src/{view → scales}/axisResolution.d.ts +12 -14
  110. package/dist/src/scales/axisResolution.d.ts.map +1 -0
  111. package/dist/src/{view → scales}/axisResolution.js +38 -12
  112. package/dist/src/scales/axisResolution.test.d.ts.map +1 -0
  113. package/dist/src/scales/scaleDomainAggregator.d.ts +57 -0
  114. package/dist/src/scales/scaleDomainAggregator.d.ts.map +1 -0
  115. package/dist/src/scales/scaleDomainAggregator.js +162 -0
  116. package/dist/src/scales/scaleDomainAggregator.test.d.ts +2 -0
  117. package/dist/src/scales/scaleDomainAggregator.test.d.ts.map +1 -0
  118. package/dist/src/scales/scaleInstanceManager.d.ts +40 -0
  119. package/dist/src/scales/scaleInstanceManager.d.ts.map +1 -0
  120. package/dist/src/scales/scaleInstanceManager.js +313 -0
  121. package/dist/src/scales/scaleInstanceManager.test.d.ts +2 -0
  122. package/dist/src/scales/scaleInstanceManager.test.d.ts.map +1 -0
  123. package/dist/src/scales/scaleInteractionController.d.ts +73 -0
  124. package/dist/src/scales/scaleInteractionController.d.ts.map +1 -0
  125. package/dist/src/scales/scaleInteractionController.js +336 -0
  126. package/dist/src/scales/scaleInteractionController.test.d.ts +2 -0
  127. package/dist/src/scales/scaleInteractionController.test.d.ts.map +1 -0
  128. package/dist/src/scales/scalePropsResolver.d.ts +23 -0
  129. package/dist/src/scales/scalePropsResolver.d.ts.map +1 -0
  130. package/dist/src/scales/scalePropsResolver.js +74 -0
  131. package/dist/src/{view → scales}/scaleResolution.d.ts +53 -31
  132. package/dist/src/scales/scaleResolution.d.ts.map +1 -0
  133. package/dist/src/scales/scaleResolution.js +658 -0
  134. package/dist/src/scales/scaleResolution.test.d.ts.map +1 -0
  135. package/dist/src/scales/scaleResolutionConstants.d.ts +6 -0
  136. package/dist/src/scales/scaleResolutionConstants.d.ts.map +1 -0
  137. package/dist/src/scales/scaleResolutionConstants.js +5 -0
  138. package/dist/src/scales/scaleRules.d.ts +16 -0
  139. package/dist/src/scales/scaleRules.d.ts.map +1 -0
  140. package/dist/src/scales/scaleRules.js +103 -0
  141. package/dist/src/scales/scaleRules.test.d.ts +2 -0
  142. package/dist/src/scales/scaleRules.test.d.ts.map +1 -0
  143. package/dist/src/spec/channel.d.ts +13 -18
  144. package/dist/src/spec/sampleView.d.ts +3 -2
  145. package/dist/src/spec/scale.d.ts +6 -0
  146. package/dist/src/types/embedApi.d.ts +5 -0
  147. package/dist/src/types/scaleResolutionApi.d.ts +1 -1
  148. package/dist/src/types/viewContext.d.ts +1 -1
  149. package/dist/src/view/concatView.d.ts +18 -0
  150. package/dist/src/view/concatView.d.ts.map +1 -1
  151. package/dist/src/view/concatView.js +73 -0
  152. package/dist/src/view/concatView.test.d.ts +2 -0
  153. package/dist/src/view/concatView.test.d.ts.map +1 -0
  154. package/dist/src/view/containerMutationHelper.d.ts +74 -0
  155. package/dist/src/view/containerMutationHelper.d.ts.map +1 -0
  156. package/dist/src/view/containerMutationHelper.js +114 -0
  157. package/dist/src/view/containerView.d.ts +0 -7
  158. package/dist/src/view/containerView.d.ts.map +1 -1
  159. package/dist/src/view/containerView.js +0 -10
  160. package/dist/src/view/facetView.d.ts.map +1 -1
  161. package/dist/src/view/facetView.js +0 -14
  162. package/dist/src/view/flowBuilder.d.ts +2 -2
  163. package/dist/src/view/flowBuilder.d.ts.map +1 -1
  164. package/dist/src/view/flowBuilder.js +21 -4
  165. package/dist/src/view/gridView/gridChild.d.ts +11 -0
  166. package/dist/src/view/gridView/gridChild.d.ts.map +1 -1
  167. package/dist/src/view/gridView/gridChild.js +32 -6
  168. package/dist/src/view/gridView/gridView.d.ts +39 -1
  169. package/dist/src/view/gridView/gridView.d.ts.map +1 -1
  170. package/dist/src/view/gridView/gridView.js +113 -42
  171. package/dist/src/view/gridView/gridView.test.d.ts +2 -0
  172. package/dist/src/view/gridView/gridView.test.d.ts.map +1 -0
  173. package/dist/src/view/gridView/scrollbar.d.ts +39 -8
  174. package/dist/src/view/gridView/scrollbar.d.ts.map +1 -1
  175. package/dist/src/view/gridView/scrollbar.js +184 -69
  176. package/dist/src/view/gridView/selectionRect.d.ts +8 -4
  177. package/dist/src/view/gridView/selectionRect.d.ts.map +1 -1
  178. package/dist/src/view/gridView/selectionRect.js +28 -3
  179. package/dist/src/view/gridView/selectionRect.test.d.ts +2 -0
  180. package/dist/src/view/gridView/selectionRect.test.d.ts.map +1 -0
  181. package/dist/src/view/layerView.d.ts +14 -0
  182. package/dist/src/view/layerView.d.ts.map +1 -1
  183. package/dist/src/view/layerView.js +66 -0
  184. package/dist/src/view/layerView.test.d.ts +2 -0
  185. package/dist/src/view/layerView.test.d.ts.map +1 -0
  186. package/dist/src/view/paramMediator.d.ts +2 -1
  187. package/dist/src/view/paramMediator.d.ts.map +1 -1
  188. package/dist/src/view/paramMediator.js +13 -1
  189. package/dist/src/view/testUtils.d.ts.map +1 -1
  190. package/dist/src/view/testUtils.js +18 -5
  191. package/dist/src/view/unitView.d.ts.map +1 -1
  192. package/dist/src/view/unitView.js +52 -12
  193. package/dist/src/view/view.d.ts +23 -7
  194. package/dist/src/view/view.d.ts.map +1 -1
  195. package/dist/src/view/view.js +61 -5
  196. package/dist/src/view/viewDispose.test.d.ts +2 -0
  197. package/dist/src/view/viewDispose.test.d.ts.map +1 -0
  198. package/dist/src/view/viewUtils.d.ts +4 -4
  199. package/dist/src/view/viewUtils.d.ts.map +1 -1
  200. package/dist/src/view/viewUtils.js +19 -15
  201. package/dist/src/view/viewUtils.test.d.ts +2 -0
  202. package/dist/src/view/viewUtils.test.d.ts.map +1 -0
  203. package/package.json +10 -10
  204. package/dist/bundle/__vite-browser-external-C--ziKoh.js +0 -8
  205. package/dist/bundle/_commonjsHelpers-DjF3Plf2.js +0 -26
  206. package/dist/bundle/index-5ajWdKly.js +0 -1319
  207. package/dist/bundle/index-B03-Om4z.js +0 -274
  208. package/dist/bundle/index-BftNdA0O.js +0 -27
  209. package/dist/bundle/index-Bg7C4Xat.js +0 -2750
  210. package/dist/bundle/index-C3QR8Lv6.js +0 -2131
  211. package/dist/bundle/index-DTcHjAHp.js +0 -505
  212. package/dist/bundle/index-DnIkxb0L.js +0 -1025
  213. package/dist/bundle/index-Ww3TAo6_.js +0 -71
  214. package/dist/bundle/index-g8iXgW0W.js +0 -651
  215. package/dist/bundle/long-B-FASCSo.js +0 -2387
  216. package/dist/bundle/remoteFile-BuaqFGWk.js +0 -94
  217. package/dist/src/data/collector.test.js +0 -138
  218. package/dist/src/data/dataFlow.test.js +0 -5
  219. package/dist/src/data/flow.test.js +0 -81
  220. package/dist/src/data/flowNode.test.js +0 -50
  221. package/dist/src/data/flowOptimizer.test.js +0 -204
  222. package/dist/src/data/formats/fasta.test.js +0 -27
  223. package/dist/src/data/sources/inlineSource.test.js +0 -63
  224. package/dist/src/data/sources/sequenceSource.test.js +0 -81
  225. package/dist/src/data/transforms/aggregate.test.js +0 -134
  226. package/dist/src/data/transforms/clone.test.js +0 -11
  227. package/dist/src/data/transforms/coverage.test.js +0 -238
  228. package/dist/src/data/transforms/filter.test.js +0 -20
  229. package/dist/src/data/transforms/flatten.test.js +0 -96
  230. package/dist/src/data/transforms/flattenDelimited.test.js +0 -90
  231. package/dist/src/data/transforms/flattenSequence.test.js +0 -34
  232. package/dist/src/data/transforms/formula.test.js +0 -25
  233. package/dist/src/data/transforms/identifier.test.js +0 -92
  234. package/dist/src/data/transforms/pileup.test.js +0 -70
  235. package/dist/src/data/transforms/project.test.js +0 -32
  236. package/dist/src/data/transforms/regexExtract.test.js +0 -70
  237. package/dist/src/data/transforms/regexFold.test.js +0 -201
  238. package/dist/src/data/transforms/sample.test.js +0 -38
  239. package/dist/src/data/transforms/stack.test.js +0 -91
  240. package/dist/src/encoder/accessor.test.js +0 -162
  241. package/dist/src/encoder/encoder.test.js +0 -105
  242. package/dist/src/genome/genome.test.js +0 -268
  243. package/dist/src/genome/genomes.test.js +0 -8
  244. package/dist/src/genome/scaleIndex.test.js +0 -78
  245. package/dist/src/genome/scaleLocus.test.js +0 -4
  246. package/dist/src/scale/scale.test.js +0 -326
  247. package/dist/src/scale/ticks.test.js +0 -46
  248. package/dist/src/selection/selection.test.js +0 -14
  249. package/dist/src/utils/addBaseUrl.test.js +0 -30
  250. package/dist/src/utils/binnedIndex.test.js +0 -201
  251. package/dist/src/utils/cloner.test.js +0 -35
  252. package/dist/src/utils/coalesce.test.js +0 -16
  253. package/dist/src/utils/concatIterables.test.js +0 -8
  254. package/dist/src/utils/domainArray.test.js +0 -130
  255. package/dist/src/utils/indexer.test.js +0 -49
  256. package/dist/src/utils/interactionEvent.test.js +0 -35
  257. package/dist/src/utils/iterateNestedMaps.test.js +0 -33
  258. package/dist/src/utils/kWayMerge.test.js +0 -30
  259. package/dist/src/utils/mergeObjects.test.js +0 -42
  260. package/dist/src/utils/numberExtractor.test.js +0 -6
  261. package/dist/src/utils/propertyCacher.test.js +0 -89
  262. package/dist/src/utils/propertyCoalescer.test.js +0 -25
  263. package/dist/src/utils/radixSort.test.js +0 -51
  264. package/dist/src/utils/reservationMap.test.js +0 -20
  265. package/dist/src/utils/ringBuffer.test.js +0 -39
  266. package/dist/src/utils/topK.test.js +0 -54
  267. package/dist/src/utils/trees.test.js +0 -135
  268. package/dist/src/utils/url.test.js +0 -28
  269. package/dist/src/utils/variableTools.test.js +0 -13
  270. package/dist/src/view/axisResolution.d.ts.map +0 -1
  271. package/dist/src/view/axisResolution.test.d.ts.map +0 -1
  272. package/dist/src/view/axisResolution.test.js +0 -206
  273. package/dist/src/view/flowBuilder.test.js +0 -125
  274. package/dist/src/view/layout/flexLayout.test.js +0 -323
  275. package/dist/src/view/layout/grid.test.js +0 -71
  276. package/dist/src/view/layout/rectangle.test.js +0 -192
  277. package/dist/src/view/paramMediator.test.js +0 -260
  278. package/dist/src/view/scaleResolution.d.ts.map +0 -1
  279. package/dist/src/view/scaleResolution.js +0 -1049
  280. package/dist/src/view/scaleResolution.test.d.ts.map +0 -1
  281. package/dist/src/view/scaleResolution.test.js +0 -645
  282. package/dist/src/view/view.test.js +0 -245
  283. package/dist/src/view/viewFactory.test.js +0 -25
  284. /package/dist/src/{view → scales}/axisResolution.test.d.ts +0 -0
  285. /package/dist/src/{view → scales}/scaleResolution.test.d.ts +0 -0
@@ -0,0 +1,658 @@
1
+ import scaleLocus, {
2
+ fromComplexInterval as locusFromComplexInterval,
3
+ fromComplexValue,
4
+ getGenomeExtent,
5
+ toComplexInterval,
6
+ toComplexValue,
7
+ } from "../genome/scaleLocus.js";
8
+ import scaleIndex from "../genome/scaleIndex.js";
9
+ import scaleNull from "../utils/scaleNull.js";
10
+
11
+ import { scale as vegaScale, isDiscrete, isContinuous } from "vega-scale";
12
+ import { configureDomain } from "../scale/scale.js";
13
+
14
+ import ScaleInstanceManager from "./scaleInstanceManager.js";
15
+ import { resolveScalePropsBase } from "./scalePropsResolver.js";
16
+ import ScaleDomainAggregator from "./scaleDomainAggregator.js";
17
+ import ScaleInteractionController from "./scaleInteractionController.js";
18
+ import {
19
+ INDEX,
20
+ LOCUS,
21
+ NOMINAL,
22
+ ORDINAL,
23
+ QUANTITATIVE,
24
+ } from "./scaleResolutionConstants.js";
25
+
26
+ import { isSecondaryChannel } from "../encoder/encoder.js";
27
+ import { NominalDomain } from "../utils/domainArray.js";
28
+ import { asArray, shallowArrayEquals } from "../utils/arrayUtils.js";
29
+
30
+ // Register scaleLocus to Vega-Scale.
31
+ // Loci are discrete but the scale's domain can be adjusted in a continuous manner.
32
+ vegaScale("index", scaleIndex, ["continuous"]);
33
+ vegaScale("locus", scaleLocus, ["continuous"]);
34
+ vegaScale("null", scaleNull, []);
35
+
36
+ export { INDEX, LOCUS, NOMINAL, ORDINAL, QUANTITATIVE };
37
+
38
+ /**
39
+ * @template {ChannelWithScale}[T=ChannelWithScale]
40
+ *
41
+ * @typedef {object} ScaleResolutionMember
42
+ * @prop {import("../view/unitView.js").default} view TODO: Get rid of the view reference
43
+ * @prop {T} channel
44
+ * @prop {import("../spec/channel.js").ChannelDefWithScale} channelDef
45
+ * @prop {(channel: ChannelWithScale, type: import("../spec/channel.js").Type) => DomainArray} dataDomainSource
46
+ */
47
+ /**
48
+ * Resolves a shared scale for a channel by merging scale properties and domains
49
+ * across participating views, then coordinating range updates and zoom/pan
50
+ * interactions. It is the central wiring point for scale-related state and
51
+ * notifications, while delegating domain aggregation, scale instance setup, and
52
+ * interaction logic to focused helpers.
53
+ *
54
+ * @implements {ScaleResolutionApi}
55
+ */
56
+ export default class ScaleResolution {
57
+ /**
58
+ * @typedef {import("../types/scaleResolutionApi.js").ScaleResolutionApi} ScaleResolutionApi
59
+ * @typedef {import("../types/scaleResolutionApi.js").ScaleResolutionEventType} ScaleResolutionEventType
60
+ * @typedef {import("../spec/channel.js").Channel} Channel
61
+ * @typedef {import("../spec/channel.js").ChannelWithScale} ChannelWithScale
62
+ * @typedef {import("../spec/scale.js").NumericDomain} NumericDomain
63
+ * @typedef {import("../spec/scale.js").ScalarDomain} ScalarDomain
64
+ * @typedef {import("../spec/scale.js").ComplexDomain} ComplexDomain
65
+ * @typedef {import("../view/unitView.js").default} UnitView
66
+ * @typedef {import("../types/encoder.js").VegaScale} VegaScale
67
+ * @typedef {import("../utils/domainArray.js").DomainArray} DomainArray
68
+ * @typedef {import("../genome/genome.js").ChromosomalLocus} ChromosomalLocus
69
+ * @typedef {import("../types/scaleResolutionApi.js").ScaleResolutionListener} ScaleResolutionListener
70
+ *
71
+ * @typedef {VegaScale & { props: import("../spec/scale.js").Scale }} ScaleWithProps
72
+ */
73
+
74
+ /** @type {Set<ScaleResolutionMember>} The involved views */
75
+ #members = new Set();
76
+
77
+ /**
78
+ * @type {Record<ScaleResolutionEventType, Set<ScaleResolutionListener>>}
79
+ */
80
+ #listeners = {
81
+ domain: new Set(),
82
+ range: new Set(),
83
+ };
84
+
85
+ /** @type {ScaleInstanceManager} */
86
+ #scaleManager;
87
+
88
+ /** @type {ScaleDomainAggregator} */
89
+ #domainAggregator;
90
+
91
+ /** @type {ScaleInteractionController} */
92
+ #interactionController;
93
+
94
+ /**
95
+ * @param {Channel} channel
96
+ */
97
+ constructor(channel) {
98
+ this.channel = channel;
99
+ /** @type {import("../spec/channel.js").Type} Data type (quantitative, nominal, etc...) */
100
+ this.type = null;
101
+
102
+ /** @type {string} An optional unique identifier for the scale */
103
+ this.name = undefined;
104
+
105
+ this.#domainAggregator = new ScaleDomainAggregator({
106
+ getMembers: () => this.#members,
107
+ getType: () => this.type,
108
+ getLocusExtent: () => this.#getLocusExtent(),
109
+ fromComplexInterval: this.fromComplexInterval.bind(this),
110
+ });
111
+
112
+ this.#scaleManager = new ScaleInstanceManager({
113
+ getParamMediator: () => this.#firstMemberView.paramMediator,
114
+ onRangeChange: () => this.#notifyListeners("range"),
115
+ onDomainChange: () => this.#notifyListeners("domain"),
116
+ getGenomeStore: () => this.#viewContext.genomeStore,
117
+ });
118
+
119
+ this.#interactionController = new ScaleInteractionController({
120
+ getScale: () => this.getScale(),
121
+ getAnimator: () => this.#viewContext.animator,
122
+ getInitialDomainSnapshot: () =>
123
+ this.#domainAggregator.initialDomainSnapshot,
124
+ getResetDomain: () =>
125
+ this.#domainAggregator.getConfiguredOrDefaultDomain(),
126
+ fromComplexInterval: this.fromComplexInterval.bind(this),
127
+ getGenomeExtent: () => this.#getLocusExtent(),
128
+ });
129
+ }
130
+
131
+ /**
132
+ * @returns {import("../view/view.js").default}
133
+ */
134
+ get #firstMemberView() {
135
+ const first = this.#members.values().next().value;
136
+ if (!first) {
137
+ throw new Error("ScaleResolution has no members!");
138
+ }
139
+ return first.view;
140
+ }
141
+
142
+ get #viewContext() {
143
+ return this.#firstMemberView.context;
144
+ }
145
+
146
+ get zoomExtent() {
147
+ return (
148
+ (this.#scaleManager.scale &&
149
+ isContinuous(this.#scaleManager.scale.type) &&
150
+ this.#interactionController.getZoomExtent()) ?? [
151
+ -Infinity,
152
+ Infinity,
153
+ ]
154
+ );
155
+ }
156
+
157
+ /**
158
+ * @returns {number[]}
159
+ */
160
+ #getLocusExtent() {
161
+ return getGenomeExtent(this.#getGenomeSource());
162
+ }
163
+
164
+ /**
165
+ * @returns {import("../genome/scaleLocus.js").GenomeSource}
166
+ */
167
+ #getGenomeSource() {
168
+ if (this.type !== LOCUS) {
169
+ return undefined;
170
+ }
171
+ return /** @type {import("../genome/scaleLocus.js").GenomeSource} */ (
172
+ this.#scaleManager.scale ?? this.#scaleManager.getLocusGenome()
173
+ );
174
+ }
175
+
176
+ /**
177
+ * Adds a listener that is called when the scale domain is changed,
178
+ * e.g., zoomed. The call is synchronous and happens before the views
179
+ * are rendered.
180
+ *
181
+ * @param {ScaleResolutionEventType} type
182
+ * @param {ScaleResolutionListener} listener function
183
+ */
184
+ addEventListener(type, listener) {
185
+ this.#listeners[type].add(listener);
186
+ }
187
+
188
+ /**
189
+ * @param {ScaleResolutionEventType} type
190
+ * @param {ScaleResolutionListener} listener function
191
+ */
192
+ removeEventListener(type, listener) {
193
+ this.#listeners[type].delete(listener);
194
+ }
195
+
196
+ /**
197
+ * @param {ScaleResolutionEventType} type
198
+ */
199
+ #notifyListeners(type) {
200
+ for (const listener of this.#listeners[type].values()) {
201
+ listener({
202
+ type,
203
+ scaleResolution: this,
204
+ });
205
+ }
206
+ }
207
+
208
+ /**
209
+ * Add a view to this resolution.
210
+ * N.B. This is expected to be called in depth-first order
211
+ *
212
+ * @param {ScaleResolutionMember} newMember
213
+ */
214
+ #addMember(newMember) {
215
+ const { channel, channelDef } = newMember;
216
+
217
+ // A convenience hack for cases where the new member should adapt
218
+ // the scale type to the existing one. For example: SelectionRect
219
+ // TODO: Add test
220
+ const adapt = channelDef.type == null && this.type;
221
+
222
+ if (
223
+ // @ts-expect-error "sample" is not really a channel with scale
224
+ channel != "sample" &&
225
+ !channelDef.type &&
226
+ !isSecondaryChannel(channel) &&
227
+ !adapt
228
+ ) {
229
+ throw new Error(
230
+ `The "type" property must be defined in channel definition: "${channel}": ${JSON.stringify(
231
+ channelDef
232
+ )}. Must be one of: "quantitative", "ordinal", "nominal", "locus", "index"`
233
+ );
234
+ }
235
+
236
+ // A hack for sample channel, which really doesn't have a scale but the
237
+ // domain is needed when samples are not specified explicitly.
238
+ // @ts-expect-error "sample" is not really a channel with scale
239
+ const type = channel == "sample" ? "nominal" : channelDef.type;
240
+ const name = channelDef?.scale?.name;
241
+
242
+ if (name) {
243
+ if (this.name !== undefined && name != this.name) {
244
+ throw new Error(
245
+ `Shared scales have conflicting names: "${name}" vs. "${this.name}"!`
246
+ );
247
+ }
248
+ this.name = name;
249
+ }
250
+
251
+ if (!adapt) {
252
+ if (!this.type) {
253
+ this.type = type;
254
+ } else if (type !== this.type && !isSecondaryChannel(channel)) {
255
+ // TODO: Include a reference to the layer
256
+ throw new Error(
257
+ `Can not use shared scale for different data types: ${this.type} vs. ${type}. Use "resolve: independent" for channel ${this.channel}`
258
+ );
259
+ // Actually, point scale could be changed into band scale
260
+ // TODO: Use the same merging logic as in: https://github.com/vega/vega-lite/blob/master/src/scale.ts
261
+ }
262
+ }
263
+
264
+ this.#members.add(newMember);
265
+ }
266
+
267
+ /**
268
+ * @param {ScaleResolutionMember} member
269
+ * @returns {() => boolean}
270
+ */
271
+ registerMember(member) {
272
+ this.#addMember(member);
273
+ return () => {
274
+ const removed = this.#members.delete(member);
275
+ return removed && this.#members.size === 0;
276
+ };
277
+ }
278
+
279
+ /**
280
+ * Returns true if the domain has been defined explicitly, i.e. not extracted from the data.
281
+ */
282
+ #isExplicitDomain() {
283
+ return this.#domainAggregator.hasConfiguredDomain();
284
+ }
285
+
286
+ #isDomainInitialized() {
287
+ const s = this.#scaleManager.scale;
288
+ if (!s) {
289
+ return false;
290
+ }
291
+
292
+ const domain = s.domain();
293
+
294
+ // We could alternatively have a flag that is set when the domain is initialized.
295
+ if (isContinuous(s.type)) {
296
+ return (
297
+ domain.length > 2 ||
298
+ (domain.length == 2 && (domain[0] !== 0 || domain[1] !== 0))
299
+ );
300
+ } else {
301
+ return domain.length > 0;
302
+ }
303
+ }
304
+
305
+ /**
306
+ * Collects and merges scale properties from the participating views.
307
+ * Does not include inferred default values such as schemes etc.
308
+ *
309
+ * @returns {import("../spec/scale.js").Scale}
310
+ */
311
+ #getMergedScaleProps() {
312
+ return resolveScalePropsBase({
313
+ channel: this.channel,
314
+ dataType: this.type,
315
+ members: this.#members,
316
+ isExplicitDomain: this.#isExplicitDomain(),
317
+ });
318
+ }
319
+
320
+ /**
321
+ * Returns the merged scale properties supplemented with inferred properties
322
+ * and domain.
323
+ *
324
+ * @param {boolean} [extractDataDomain]
325
+ * @returns {import("../spec/scale.js").Scale}
326
+ */
327
+ #getScaleProps(extractDataDomain = false) {
328
+ const props = this.#getMergedScaleProps();
329
+ if (props === null || props.type == "null") {
330
+ // No scale (pass-thru)
331
+ // TODO: Check that the channel is compatible
332
+ return { type: "null" };
333
+ }
334
+
335
+ const domain =
336
+ this.#domainAggregator.getConfiguredOrDefaultDomain(
337
+ extractDataDomain
338
+ );
339
+
340
+ if (domain && domain.length > 0) {
341
+ props.domain = domain;
342
+ } else if (isDiscrete(props.type)) {
343
+ props.domain = new NominalDomain();
344
+ }
345
+
346
+ if (!props.domain && props.domainMid !== undefined) {
347
+ // Initialize with a bogus domain so that scale.js can inject the domainMid.
348
+ // The number of domain elements must be know before the glsl scale is generated.
349
+ props.domain = [props.domainMin ?? 0, props.domainMax ?? 1];
350
+ }
351
+
352
+ return props;
353
+ }
354
+
355
+ /**
356
+ * Reconfigures the scale: updates domain and other settings.
357
+ *
358
+ * Use this when the set of participating members changes (views added or removed),
359
+ * or when scale properties are otherwise re-resolved from the view hierarchy.
360
+ */
361
+ reconfigure() {
362
+ const props = this.#getScaleProps(true);
363
+ this.#reconfigureWith(() => this.#scaleManager.reconfigureScale(props));
364
+ }
365
+
366
+ /**
367
+ * Reconfigures only the effective domain (configured + data-derived).
368
+ *
369
+ * Use this when data changes but the scale membership and properties are stable.
370
+ */
371
+ reconfigureDomain() {
372
+ const props = this.#getScaleProps(true);
373
+ this.#reconfigureWith(() => {
374
+ configureDomain(this.#scaleManager.scale, props);
375
+ });
376
+ }
377
+
378
+ /**
379
+ * @param {() => void} apply
380
+ */
381
+ #reconfigureWith(apply) {
382
+ const scale = this.#scaleManager.scale;
383
+
384
+ if (!scale || scale.type == "null") {
385
+ return;
386
+ }
387
+
388
+ const domainWasInitialized = this.#isDomainInitialized();
389
+ const previousDomain = scale.domain();
390
+
391
+ this.#scaleManager.withDomainNotificationsSuppressed(apply);
392
+
393
+ if (
394
+ this.#domainAggregator.captureInitialDomain(
395
+ scale,
396
+ domainWasInitialized
397
+ )
398
+ ) {
399
+ // Domain changes were suppressed during reconfigure; notify explicitly.
400
+ this.#notifyListeners("domain");
401
+ return;
402
+ }
403
+
404
+ const newDomain = scale.domain();
405
+ if (!shallowArrayEquals(newDomain, previousDomain)) {
406
+ if (this.isZoomable()) {
407
+ // Don't mess with zoomed views, restore the previous domain
408
+ this.#scaleManager.withDomainNotificationsSuppressed(() => {
409
+ scale.domain(previousDomain);
410
+ });
411
+ } else if (this.#interactionController.isZoomingSupported()) {
412
+ // It can be zoomed, so lets make a smooth transition.
413
+ // Restore the previous domain and zoom smoothly to the new domain.
414
+ this.#scaleManager.withDomainNotificationsSuppressed(() => {
415
+ scale.domain(previousDomain);
416
+ });
417
+ this.zoomTo(newDomain, 500); // TODO: Configurable duration
418
+ } else {
419
+ // Update immediately if the previous domain was the initial domain [0, 0]
420
+ // Notifications were suppressed during reconfigure; notify explicitly.
421
+ this.#notifyListeners("domain");
422
+ }
423
+ }
424
+ }
425
+
426
+ /**
427
+ * @returns {ScaleWithProps}
428
+ */
429
+ get scale() {
430
+ if (this.#scaleManager.scale) {
431
+ return this.#scaleManager.scale;
432
+ }
433
+ throw new Error(
434
+ "ScaleResolution.scale accessed before initialization. Call initializeScale()."
435
+ );
436
+ }
437
+
438
+ /**
439
+ * Returns the scale instance, creating it if needed.
440
+ *
441
+ * Use this from call sites that may run before explicit initialization.
442
+ *
443
+ * @returns {ScaleWithProps}
444
+ */
445
+ getScale() {
446
+ return this.#scaleManager.scale ?? this.initializeScale();
447
+ }
448
+
449
+ /**
450
+ * Initializes the scale instance once resolution has stabilized.
451
+ *
452
+ * @returns {ScaleWithProps}
453
+ */
454
+ initializeScale() {
455
+ if (this.#scaleManager.scale) {
456
+ return this.#scaleManager.scale;
457
+ }
458
+
459
+ const props = this.#getScaleProps();
460
+ const scale = this.#scaleManager.createScale(props);
461
+
462
+ return scale;
463
+ }
464
+
465
+ getDomain() {
466
+ return this.getScale().domain();
467
+ }
468
+
469
+ /**
470
+ * Extracts and unions the data domains of all participating views.
471
+ *
472
+ * @return { DomainArray }
473
+ */
474
+ getDataDomain() {
475
+ return this.#domainAggregator.getDataDomain();
476
+ }
477
+
478
+ /**
479
+ * @returns {NumericDomain | ComplexDomain}
480
+ */
481
+ getComplexDomain() {
482
+ return /** @type {NumericDomain | ComplexDomain} */ (
483
+ toComplexInterval(this.#getGenomeSource(), this.getDomain())
484
+ );
485
+ }
486
+
487
+ /**
488
+ * Return true if the scale is zoomable and the current domain differs from the initial domain.
489
+ *
490
+ * @returns true if zoomed
491
+ */
492
+ isZoomed() {
493
+ return this.#interactionController.isZoomed();
494
+ }
495
+
496
+ /**
497
+ * Returns true if zooming is supported and allowed in view spec.
498
+ */
499
+ isZoomable() {
500
+ // Check explicit configuration
501
+ return this.#interactionController.isZoomable();
502
+ }
503
+
504
+ /**
505
+ * Pans (translates) and zooms using a specified scale factor.
506
+ *
507
+ * @param {number} scaleFactor
508
+ * @param {number} scaleAnchor
509
+ * @param {number} pan
510
+ * @returns {boolean} true if the scale was zoomed
511
+ */
512
+ zoom(scaleFactor, scaleAnchor, pan) {
513
+ return this.#interactionController.zoom(scaleFactor, scaleAnchor, pan);
514
+ }
515
+
516
+ /**
517
+ * Immediately zooms to the given interval.
518
+ *
519
+ * @param {NumericDomain | ComplexDomain} domain
520
+ * @param {boolean | number} [duration] an approximate duration for transition.
521
+ * Zero duration zooms immediately. Boolean `true` indicates a default duration.
522
+ */
523
+ async zoomTo(domain, duration = false) {
524
+ return this.#interactionController.zoomTo(domain, duration);
525
+ }
526
+
527
+ /**
528
+ * Resets the current domain to the initial one
529
+ *
530
+ * @returns true if the domain was changed
531
+ */
532
+ resetZoom() {
533
+ return this.#interactionController.resetZoom();
534
+ }
535
+
536
+ /**
537
+ * Returns the zoom level with respect to the reference domain span (the original domain).
538
+ *
539
+ * In principle, this is highly specific to positional channels. However, zooming can
540
+ * be generalized to other quantitative channels such as color, opacity, size, etc.
541
+ */
542
+ getZoomLevel() {
543
+ return this.#interactionController.getZoomLevel();
544
+ }
545
+
546
+ /**
547
+ * Returns the length of the axis in pixels. Chooses the smallest of the views.
548
+ * They should all be the same, but some exotic configuration might break that assumption.
549
+ *
550
+ * This method is needed because positional channels have unit ranges and the
551
+ * length of the axis is not directly available from the scale. Ideally, ranges would
552
+ * be configured as pixels, but that is yet to be materialized.
553
+ */
554
+ getAxisLength() {
555
+ if (this.channel !== "x" && this.channel !== "y") {
556
+ throw new Error(
557
+ "Axis length is only defined for x and y channels!"
558
+ );
559
+ }
560
+
561
+ // Here's a problem: if the view has been hidden, it may have stale coords.
562
+ // TODO: They should be cleared when the layout is invalidated.
563
+ // Alternatively, scale ranges could be set in pixels.
564
+ const lengths = Array.from(this.#members)
565
+ .map(
566
+ (m) =>
567
+ m.view.coords?.[this.channel === "x" ? "width" : "height"]
568
+ )
569
+ .filter((len) => len > 0);
570
+
571
+ return lengths.length
572
+ ? lengths.reduce((a, b) => Math.min(a, b), 10000)
573
+ : 0;
574
+ }
575
+
576
+ /**
577
+ * Inverts a value in range to a value on domain. Returns an object in
578
+ * case of locus scale.
579
+ *
580
+ * @param {number} value
581
+ */
582
+ invertToComplex(value) {
583
+ const scale = this.getScale();
584
+ if ("invert" in scale) {
585
+ const inverted = /** @type {number} */ (scale.invert(value));
586
+ return this.toComplex(inverted);
587
+ } else {
588
+ throw new Error("The scale does not support inverting!");
589
+ }
590
+ }
591
+
592
+ /**
593
+ * @param {number} value
594
+ */
595
+ toComplex(value) {
596
+ return toComplexValue(this.#getGenomeSource(), value);
597
+ }
598
+
599
+ /**
600
+ * @param {number | ChromosomalLocus} complex
601
+ * @returns {number}
602
+ */
603
+ fromComplex(complex) {
604
+ return fromComplexValue(this.#getGenomeSource(), complex);
605
+ }
606
+
607
+ /**
608
+ * @param {ScalarDomain | ComplexDomain} interval
609
+ * @returns {number[]}
610
+ */
611
+ fromComplexInterval(interval) {
612
+ if (this.type == LOCUS) {
613
+ return locusFromComplexInterval(this.#getGenomeSource(), interval);
614
+ }
615
+ return /** @type {number[]} */ (interval);
616
+ }
617
+ }
618
+
619
+ /**
620
+ * Reconfigures scale domains, starting from the given view.
621
+ *
622
+ * Use this for data-driven updates where only domains need refreshing.
623
+ *
624
+ * TODO: This should be made unnecessary. Collectors should trigger the reconfiguration
625
+ * for those views that get their data from the collector.
626
+ *
627
+ * TODO: This may reconfigure channels that are not affected by the change.
628
+ * Causes performance issues with domains that are extracted from data.
629
+ *
630
+ * @param {import("../view/view.js").default | import("../view/view.js").default[]} fromViews
631
+ */
632
+ export function reconfigureScaleDomains(fromViews) {
633
+ /** @type {Set<ScaleResolution>} */
634
+ const uniqueResolutions = new Set();
635
+
636
+ /** @param {import("../view/view.js").default} view */
637
+ function collectResolutions(view) {
638
+ for (const resolution of Object.values(view.resolutions.scale)) {
639
+ uniqueResolutions.add(resolution);
640
+ }
641
+ }
642
+
643
+ for (const fromView of asArray(fromViews)) {
644
+ // Descendants
645
+ fromView.visit(collectResolutions);
646
+
647
+ // Ancestors
648
+ for (const view of fromView.getDataAncestors()) {
649
+ // Skip axis views etc. They should not mess with the domains.
650
+ if (!view.options.contributesToScaleDomain) {
651
+ break;
652
+ }
653
+ collectResolutions(view);
654
+ }
655
+ }
656
+
657
+ uniqueResolutions.forEach((resolution) => resolution.reconfigureDomain());
658
+ }
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scaleResolution.test.d.ts","sourceRoot":"","sources":["../../../src/scales/scaleResolution.test.js"],"names":[],"mappings":"sBAYa,OAAO,oBAAoB,EAAE,OAAO"}
@@ -0,0 +1,6 @@
1
+ export const QUANTITATIVE: "quantitative";
2
+ export const ORDINAL: "ordinal";
3
+ export const NOMINAL: "nominal";
4
+ export const LOCUS: "locus";
5
+ export const INDEX: "index";
6
+ //# sourceMappingURL=scaleResolutionConstants.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scaleResolutionConstants.d.ts","sourceRoot":"","sources":["../../../src/scales/scaleResolutionConstants.js"],"names":[],"mappings":"AAAA,2BAA4B,cAAc,CAAC;AAC3C,sBAAuB,SAAS,CAAC;AACjC,sBAAuB,SAAS,CAAC;AACjC,oBAAqB,OAAO,CAAC;AAC7B,oBAAqB,OAAO,CAAC"}
@@ -0,0 +1,5 @@
1
+ export const QUANTITATIVE = "quantitative";
2
+ export const ORDINAL = "ordinal";
3
+ export const NOMINAL = "nominal";
4
+ export const LOCUS = "locus";
5
+ export const INDEX = "index";
@@ -0,0 +1,16 @@
1
+ /**
2
+ * @typedef {import("../spec/channel.js").Channel} Channel
3
+ */
4
+ /**
5
+ * @param {Channel} channel
6
+ * @param {import("../spec/channel.js").Type} dataType
7
+ * @returns {import("../spec/scale.js").ScaleType}
8
+ */
9
+ export function getDefaultScaleType(channel: Channel, dataType: import("../spec/channel.js").Type): import("../spec/scale.js").ScaleType;
10
+ /**
11
+ * @param {import("../spec/scale.js").Scale} props
12
+ * @param {Channel} channel
13
+ */
14
+ export function applyLockedProperties(props: import("../spec/scale.js").Scale, channel: Channel): void;
15
+ export type Channel = import("../spec/channel.js").Channel;
16
+ //# sourceMappingURL=scaleRules.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scaleRules.d.ts","sourceRoot":"","sources":["../../../src/scales/scaleRules.js"],"names":[],"mappings":"AAcA;;GAEG;AAEH;;;;GAIG;AACH,6CAJW,OAAO,YACP,OAAO,oBAAoB,EAAE,IAAI,GAC/B,OAAO,kBAAkB,EAAE,SAAS,CAkEhD;AAED;;;GAGG;AACH,6CAHW,OAAO,kBAAkB,EAAE,KAAK,WAChC,OAAO,QAWjB;sBAvFY,OAAO,oBAAoB,EAAE,OAAO"}