@genome-spy/core 0.48.1 → 0.49.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 (241) hide show
  1. package/dist/bundle/index.es.js +7485 -7097
  2. package/dist/bundle/index.js +124 -111
  3. package/dist/schema.json +838 -344
  4. package/dist/src/data/collector.d.ts +10 -8
  5. package/dist/src/data/collector.d.ts.map +1 -1
  6. package/dist/src/data/collector.js +131 -33
  7. package/dist/src/data/collector.test.d.ts +2 -0
  8. package/dist/src/data/collector.test.d.ts.map +1 -0
  9. package/dist/src/data/collector.test.js +55 -1
  10. package/dist/src/data/dataFlow.test.d.ts +2 -0
  11. package/dist/src/data/dataFlow.test.d.ts.map +1 -0
  12. package/dist/src/data/flow.test.d.ts +2 -0
  13. package/dist/src/data/flow.test.d.ts.map +1 -0
  14. package/dist/src/data/flow.test.js +19 -14
  15. package/dist/src/data/flowNode.test.d.ts +2 -0
  16. package/dist/src/data/flowNode.test.d.ts.map +1 -0
  17. package/dist/src/data/flowOptimizer.test.d.ts +2 -0
  18. package/dist/src/data/flowOptimizer.test.d.ts.map +1 -0
  19. package/dist/src/data/flowOptimizer.test.js +9 -10
  20. package/dist/src/data/formats/fasta.test.d.ts +2 -0
  21. package/dist/src/data/formats/fasta.test.d.ts.map +1 -0
  22. package/dist/src/data/sources/inlineSource.test.d.ts +2 -0
  23. package/dist/src/data/sources/inlineSource.test.d.ts.map +1 -0
  24. package/dist/src/data/sources/inlineSource.test.js +23 -16
  25. package/dist/src/data/sources/sequenceSource.test.d.ts +2 -0
  26. package/dist/src/data/sources/sequenceSource.test.d.ts.map +1 -0
  27. package/dist/src/data/sources/sequenceSource.test.js +59 -42
  28. package/dist/src/data/transforms/clone.test.d.ts +2 -0
  29. package/dist/src/data/transforms/clone.test.d.ts.map +1 -0
  30. package/dist/src/data/transforms/coverage.test.d.ts +2 -0
  31. package/dist/src/data/transforms/coverage.test.d.ts.map +1 -0
  32. package/dist/src/data/transforms/coverage.test.js +1 -1
  33. package/dist/src/data/transforms/filter.d.ts +10 -0
  34. package/dist/src/data/transforms/filter.d.ts.map +1 -1
  35. package/dist/src/data/transforms/filter.js +30 -1
  36. package/dist/src/data/transforms/filter.test.d.ts +2 -0
  37. package/dist/src/data/transforms/filter.test.d.ts.map +1 -0
  38. package/dist/src/data/transforms/flatten.test.d.ts +2 -0
  39. package/dist/src/data/transforms/flatten.test.d.ts.map +1 -0
  40. package/dist/src/data/transforms/flatten.test.js +10 -7
  41. package/dist/src/data/transforms/flattenDelimited.test.d.ts +2 -0
  42. package/dist/src/data/transforms/flattenDelimited.test.d.ts.map +1 -0
  43. package/dist/src/data/transforms/flattenDelimited.test.js +16 -13
  44. package/dist/src/data/transforms/flattenSequence.test.d.ts +2 -0
  45. package/dist/src/data/transforms/flattenSequence.test.d.ts.map +1 -0
  46. package/dist/src/data/transforms/flattenSequence.test.js +1 -1
  47. package/dist/src/data/transforms/formula.test.d.ts +2 -0
  48. package/dist/src/data/transforms/formula.test.d.ts.map +1 -0
  49. package/dist/src/data/transforms/formula.test.js +1 -1
  50. package/dist/src/data/transforms/identifier.d.ts +1 -1
  51. package/dist/src/data/transforms/identifier.d.ts.map +1 -1
  52. package/dist/src/data/transforms/identifier.js +2 -2
  53. package/dist/src/data/transforms/identifier.test.d.ts +2 -0
  54. package/dist/src/data/transforms/identifier.test.d.ts.map +1 -0
  55. package/dist/src/data/transforms/identifier.test.js +23 -14
  56. package/dist/src/data/transforms/pileup.test.d.ts +2 -0
  57. package/dist/src/data/transforms/pileup.test.d.ts.map +1 -0
  58. package/dist/src/data/transforms/project.test.d.ts +2 -0
  59. package/dist/src/data/transforms/project.test.d.ts.map +1 -0
  60. package/dist/src/data/transforms/project.test.js +1 -1
  61. package/dist/src/data/transforms/regexExtract.test.d.ts +2 -0
  62. package/dist/src/data/transforms/regexExtract.test.d.ts.map +1 -0
  63. package/dist/src/data/transforms/regexExtract.test.js +6 -3
  64. package/dist/src/data/transforms/regexFold.test.d.ts +2 -0
  65. package/dist/src/data/transforms/regexFold.test.d.ts.map +1 -0
  66. package/dist/src/data/transforms/sample.test.d.ts +2 -0
  67. package/dist/src/data/transforms/sample.test.d.ts.map +1 -0
  68. package/dist/src/data/transforms/stack.test.d.ts +2 -0
  69. package/dist/src/data/transforms/stack.test.d.ts.map +1 -0
  70. package/dist/src/data/transforms/stack.test.js +8 -8
  71. package/dist/src/encoder/accessor.d.ts +17 -14
  72. package/dist/src/encoder/accessor.d.ts.map +1 -1
  73. package/dist/src/encoder/accessor.js +127 -56
  74. package/dist/src/encoder/accessor.test.d.ts +2 -0
  75. package/dist/src/encoder/accessor.test.d.ts.map +1 -0
  76. package/dist/src/encoder/accessor.test.js +145 -31
  77. package/dist/src/encoder/encoder.d.ts +26 -13
  78. package/dist/src/encoder/encoder.d.ts.map +1 -1
  79. package/dist/src/encoder/encoder.js +98 -114
  80. package/dist/src/encoder/encoder.test.d.ts +2 -0
  81. package/dist/src/encoder/encoder.test.d.ts.map +1 -0
  82. package/dist/src/encoder/encoder.test.js +85 -82
  83. package/dist/src/fonts/bmFontManager.d.ts.map +1 -1
  84. package/dist/src/fonts/bmFontManager.js +10 -4
  85. package/dist/src/genome/genome.test.d.ts +2 -0
  86. package/dist/src/genome/genome.test.d.ts.map +1 -0
  87. package/dist/src/genome/scaleIndex.test.d.ts +2 -0
  88. package/dist/src/genome/scaleIndex.test.d.ts.map +1 -0
  89. package/dist/src/genome/scaleLocus.test.d.ts +2 -0
  90. package/dist/src/genome/scaleLocus.test.d.ts.map +1 -0
  91. package/dist/src/genomeSpy.d.ts +3 -2
  92. package/dist/src/genomeSpy.d.ts.map +1 -1
  93. package/dist/src/genomeSpy.js +29 -21
  94. package/dist/src/gl/dataToVertices.d.ts +5 -7
  95. package/dist/src/gl/dataToVertices.d.ts.map +1 -1
  96. package/dist/src/gl/dataToVertices.js +42 -30
  97. package/dist/src/gl/glslScaleGenerator.d.ts +84 -15
  98. package/dist/src/gl/glslScaleGenerator.d.ts.map +1 -1
  99. package/dist/src/gl/glslScaleGenerator.js +260 -73
  100. package/dist/src/gl/includes/picking.vertex.glsl.js +1 -1
  101. package/dist/src/marks/link.common.glsl.js +1 -1
  102. package/dist/src/marks/link.d.ts.map +1 -1
  103. package/dist/src/marks/link.js +10 -0
  104. package/dist/src/marks/link.vertex.glsl.js +1 -1
  105. package/dist/src/marks/mark.d.ts +6 -9
  106. package/dist/src/marks/mark.d.ts.map +1 -1
  107. package/dist/src/marks/mark.js +212 -95
  108. package/dist/src/marks/point.d.ts.map +1 -1
  109. package/dist/src/marks/point.js +5 -1
  110. package/dist/src/marks/rect.d.ts.map +1 -1
  111. package/dist/src/marks/rect.js +9 -4
  112. package/dist/src/marks/rule.d.ts.map +1 -1
  113. package/dist/src/marks/rule.js +4 -0
  114. package/dist/src/marks/text.d.ts.map +1 -1
  115. package/dist/src/marks/text.js +5 -1
  116. package/dist/src/scale/scale.test.d.ts +2 -0
  117. package/dist/src/scale/scale.test.d.ts.map +1 -0
  118. package/dist/src/scale/scale.test.js +2 -0
  119. package/dist/src/scale/ticks.test.d.ts +2 -0
  120. package/dist/src/scale/ticks.test.d.ts.map +1 -0
  121. package/dist/src/scale/ticks.test.js +6 -0
  122. package/dist/src/selection/selection.d.ts +39 -0
  123. package/dist/src/selection/selection.d.ts.map +1 -0
  124. package/dist/src/selection/selection.js +78 -0
  125. package/dist/src/spec/channel.d.ts +137 -83
  126. package/dist/src/spec/mark.d.ts +9 -0
  127. package/dist/src/spec/parameter.d.ts +112 -3
  128. package/dist/src/spec/root.d.ts +0 -1
  129. package/dist/src/spec/transform.d.ts +19 -1
  130. package/dist/src/spec/view.d.ts +3 -3
  131. package/dist/src/tooltip/dataTooltipHandler.js +1 -1
  132. package/dist/src/types/encoder.d.ts +80 -26
  133. package/dist/src/types/rendering.d.ts +1 -0
  134. package/dist/src/types/selectionTypes.d.ts +44 -0
  135. package/dist/src/types/viewContext.d.ts +1 -4
  136. package/dist/src/utils/addBaseUrl.test.d.ts +2 -0
  137. package/dist/src/utils/addBaseUrl.test.d.ts.map +1 -0
  138. package/dist/src/utils/animator.d.ts.map +1 -1
  139. package/dist/src/utils/animator.js +3 -1
  140. package/dist/src/utils/binnedIndex.test.d.ts +2 -0
  141. package/dist/src/utils/binnedIndex.test.d.ts.map +1 -0
  142. package/dist/src/utils/cloner.test.d.ts +2 -0
  143. package/dist/src/utils/cloner.test.d.ts.map +1 -0
  144. package/dist/src/utils/coalesce.test.d.ts +2 -0
  145. package/dist/src/utils/coalesce.test.d.ts.map +1 -0
  146. package/dist/src/utils/concatIterables.test.d.ts +2 -0
  147. package/dist/src/utils/concatIterables.test.d.ts.map +1 -0
  148. package/dist/src/utils/domainArray.test.d.ts +2 -0
  149. package/dist/src/utils/domainArray.test.d.ts.map +1 -0
  150. package/dist/src/utils/expression.d.ts +2 -2
  151. package/dist/src/utils/expression.d.ts.map +1 -1
  152. package/dist/src/utils/expression.js +11 -2
  153. package/dist/src/utils/indexer.test.d.ts +2 -0
  154. package/dist/src/utils/indexer.test.d.ts.map +1 -0
  155. package/dist/src/utils/inertia.d.ts.map +1 -1
  156. package/dist/src/utils/inertia.js +4 -0
  157. package/dist/src/utils/inputBinding.d.ts.map +1 -1
  158. package/dist/src/utils/inputBinding.js +4 -0
  159. package/dist/src/utils/iterateNestedMaps.d.ts +4 -3
  160. package/dist/src/utils/iterateNestedMaps.d.ts.map +1 -1
  161. package/dist/src/utils/iterateNestedMaps.js +3 -2
  162. package/dist/src/utils/iterateNestedMaps.test.d.ts +2 -0
  163. package/dist/src/utils/iterateNestedMaps.test.d.ts.map +1 -0
  164. package/dist/src/utils/kWayMerge.test.d.ts +2 -0
  165. package/dist/src/utils/kWayMerge.test.d.ts.map +1 -0
  166. package/dist/src/utils/mergeObjects.test.d.ts +2 -0
  167. package/dist/src/utils/mergeObjects.test.d.ts.map +1 -0
  168. package/dist/src/utils/numberExtractor.test.d.ts +2 -0
  169. package/dist/src/utils/numberExtractor.test.d.ts.map +1 -0
  170. package/dist/src/utils/propertyCacher.test.d.ts +2 -0
  171. package/dist/src/utils/propertyCacher.test.d.ts.map +1 -0
  172. package/dist/src/utils/propertyCoalescer.test.d.ts +2 -0
  173. package/dist/src/utils/propertyCoalescer.test.d.ts.map +1 -0
  174. package/dist/src/utils/propertyCoalescer.test.js +3 -0
  175. package/dist/src/utils/radixSort.d.ts +9 -0
  176. package/dist/src/utils/radixSort.d.ts.map +1 -0
  177. package/dist/src/utils/radixSort.js +130 -0
  178. package/dist/src/utils/radixSort.test.d.ts +2 -0
  179. package/dist/src/utils/radixSort.test.d.ts.map +1 -0
  180. package/dist/src/utils/radixSort.test.js +51 -0
  181. package/dist/src/utils/reservationMap.test.d.ts +2 -0
  182. package/dist/src/utils/reservationMap.test.d.ts.map +1 -0
  183. package/dist/src/utils/ringBuffer.test.d.ts +2 -0
  184. package/dist/src/utils/ringBuffer.test.d.ts.map +1 -0
  185. package/dist/src/utils/topK.test.d.ts +2 -0
  186. package/dist/src/utils/topK.test.d.ts.map +1 -0
  187. package/dist/src/utils/trees.test.d.ts +2 -0
  188. package/dist/src/utils/trees.test.d.ts.map +1 -0
  189. package/dist/src/utils/trees.test.js +8 -3
  190. package/dist/src/utils/variableTools.test.d.ts +2 -0
  191. package/dist/src/utils/variableTools.test.d.ts.map +1 -0
  192. package/dist/src/view/axisResolution.d.ts +19 -6
  193. package/dist/src/view/axisResolution.d.ts.map +1 -1
  194. package/dist/src/view/axisResolution.js +16 -7
  195. package/dist/src/view/axisResolution.test.d.ts +2 -0
  196. package/dist/src/view/axisResolution.test.d.ts.map +1 -0
  197. package/dist/src/view/axisResolution.test.js +16 -11
  198. package/dist/src/view/facetView.d.ts +1 -1
  199. package/dist/src/view/facetView.d.ts.map +1 -1
  200. package/dist/src/view/flowBuilder.d.ts +1 -1
  201. package/dist/src/view/flowBuilder.d.ts.map +1 -1
  202. package/dist/src/view/flowBuilder.js +34 -5
  203. package/dist/src/view/flowBuilder.test.d.ts +2 -0
  204. package/dist/src/view/flowBuilder.test.d.ts.map +1 -0
  205. package/dist/src/view/gridView.d.ts +0 -6
  206. package/dist/src/view/gridView.d.ts.map +1 -1
  207. package/dist/src/view/layerView.d.ts +0 -6
  208. package/dist/src/view/layerView.d.ts.map +1 -1
  209. package/dist/src/view/layout/flexLayout.test.d.ts +2 -0
  210. package/dist/src/view/layout/flexLayout.test.d.ts.map +1 -0
  211. package/dist/src/view/layout/grid.test.d.ts +2 -0
  212. package/dist/src/view/layout/grid.test.d.ts.map +1 -0
  213. package/dist/src/view/layout/rectangle.test.d.ts +2 -0
  214. package/dist/src/view/layout/rectangle.test.d.ts.map +1 -0
  215. package/dist/src/view/paramMediator.d.ts +32 -5
  216. package/dist/src/view/paramMediator.d.ts.map +1 -1
  217. package/dist/src/view/paramMediator.js +97 -9
  218. package/dist/src/view/paramMediator.test.d.ts +2 -0
  219. package/dist/src/view/paramMediator.test.d.ts.map +1 -0
  220. package/dist/src/view/paramMediator.test.js +17 -1
  221. package/dist/src/view/scaleResolution.d.ts +17 -9
  222. package/dist/src/view/scaleResolution.d.ts.map +1 -1
  223. package/dist/src/view/scaleResolution.js +51 -34
  224. package/dist/src/view/scaleResolution.test.d.ts +2 -0
  225. package/dist/src/view/scaleResolution.test.d.ts.map +1 -0
  226. package/dist/src/view/scaleResolution.test.js +2 -0
  227. package/dist/src/view/testUtils.d.ts.map +1 -1
  228. package/dist/src/view/testUtils.js +15 -3
  229. package/dist/src/view/unitView.d.ts +5 -15
  230. package/dist/src/view/unitView.d.ts.map +1 -1
  231. package/dist/src/view/unitView.js +81 -101
  232. package/dist/src/view/view.d.ts +1 -1
  233. package/dist/src/view/view.d.ts.map +1 -1
  234. package/dist/src/view/view.test.d.ts +2 -0
  235. package/dist/src/view/view.test.d.ts.map +1 -0
  236. package/dist/src/view/view.test.js +73 -55
  237. package/dist/src/view/viewFactory.test.d.ts +2 -0
  238. package/dist/src/view/viewFactory.test.d.ts.map +1 -0
  239. package/dist/src/view/viewFactory.test.js +2 -2
  240. package/dist/src/view/zoom.js +2 -2
  241. package/package.json +5 -2
@@ -12,25 +12,27 @@ export default class Collector extends FlowNode {
12
12
  params: import("../spec/transform.js").CollectParams;
13
13
  /** @type {(function(Collector):void)[]} */
14
14
  observers: ((arg0: Collector) => void)[];
15
- /** @type {Map<any | any[], import("./flowNode.js").Data>} TODO: proper type for key */
16
- facetBatches: Map<any | any[], import("./flowNode.js").Data>;
17
- _init(): void;
18
- /** @type {import("./flowNode.js").Data} */
19
- _data: import("./flowNode.js").Data;
15
+ /** @type {Map<import("../spec/channel.js").Scalar[], Data>} TODO: proper type for key */
16
+ facetBatches: Map<import("../spec/channel.js").Scalar[], import("./flowNode.js").Data>;
20
17
  /**
21
- * @returns {Iterable<import("./flowNode.js").Datum>}
18
+ * @returns {Iterable<Datum>}
22
19
  */
23
20
  getData(): Iterable<import("./flowNode.js").Datum>;
24
21
  /**
25
22
  *
26
- * @param {(datum: import("./flowNode.js").Datum) => void} visitor
23
+ * @param {(datum: Datum) => void} visitor
27
24
  */
28
25
  visitData(visitor: (datum: import("./flowNode.js").Datum) => void): void;
29
26
  /**
30
27
  * Returns the total number of data items collected.
31
28
  */
32
29
  getItemCount(): number;
33
- _checkStatus(): void;
30
+ /**
31
+ * Use an index to find a datum by its unique id.
32
+ *
33
+ * @param {number} uniqueId
34
+ */
35
+ findDatumByUniqueId(uniqueId: number): import("./flowNode.js").Datum;
34
36
  #private;
35
37
  }
36
38
  import FlowNode from "./flowNode.js";
@@ -1 +1 @@
1
- {"version":3,"file":"collector.d.ts","sourceRoot":"","sources":["../../../src/data/collector.js"],"names":[],"mappings":"AAQA;;;;;GAKG;AACH;IAKI;;OAEG;IACH,qBAFW,OAAO,sBAAsB,EAAE,aAAa,EActD;IATG,qDAA2C;IAE3C,2CAA2C;IAC3C,WADW,QAAU,SAAS,KAAE,IAAI,CAAC,EAAE,CACpB;IAEnB,uFAAuF;IACvF,cADW,IAAI,GAAG,GAAG,GAAG,EAAE,EAAE,OAAO,eAAe,EAAE,IAAI,CAAC,CAC5B;IAKjC,cAOC;IANG,2CAA2C;IAC3C,OADW,OAAO,eAAe,EAAE,IAAI,CACxB;IA6GnB;;OAEG;IACH,WAFa,SAAS,OAAO,eAAe,EAAE,KAAK,CAAC,CAuBnD;IAED;;;OAGG;IACH,2BAFmB,OAAO,eAAe,EAAE,KAAK,KAAK,IAAI,QAUxD;IAED;;OAEG;IACH,uBAMC;IAED,qBAMC;;CACJ;qBAzMyD,eAAe"}
1
+ {"version":3,"file":"collector.d.ts","sourceRoot":"","sources":["../../../src/data/collector.js"],"names":[],"mappings":"AAUA;;;;;GAKG;AACH;IA8BI;;OAEG;IACH,qBAFW,OAAO,sBAAsB,EAAE,aAAa,EAetD;IAVG,qDAA2C;IAE3C,2CAA2C;IAC3C,WADW,QAAU,SAAS,KAAE,IAAI,CAAC,EAAE,CACpB;IAGnB,yFAAyF;IACzF,cADW,IAAI,OAAO,oBAAoB,EAAE,MAAM,EAAE,+BAAO,CACN;IAuHzD;;OAEG;IACH,mDAmBC;IAED;;;OAGG;IACH,6DAF6B,IAAI,QAUhC;IAED;;OAEG;IACH,uBAMC;IA8CD;;;;OAIG;IACH,8BAFW,MAAM,iCA4BhB;;CACJ;qBA3SyD,eAAe"}
@@ -1,10 +1,12 @@
1
1
  import { InternMap } from "internmap";
2
- import { group } from "d3-array";
2
+ import { bisector, group } from "d3-array";
3
3
  import { compare } from "vega-util";
4
4
  import iterateNestedMaps from "../utils/iterateNestedMaps.js";
5
5
  import FlowNode, { BEHAVIOR_COLLECTS, isFacetBatch } from "./flowNode.js";
6
6
  import { field } from "../utils/field.js";
7
7
  import { asArray } from "../utils/arrayUtils.js";
8
+ import { radixSortIntoLookupArray } from "../utils/radixSort.js";
9
+ import { UNIQUE_ID_KEY } from "./transforms/identifier.js";
8
10
 
9
11
  /**
10
12
  * Collects (materializes) the data that flows through this node.
@@ -13,6 +15,31 @@ import { asArray } from "../utils/arrayUtils.js";
13
15
  * Grouping is primarily intended for handling faceted data.
14
16
  */
15
17
  export default class Collector extends FlowNode {
18
+ /**
19
+ * @typedef {import("./flowNode.js").Datum} Datum
20
+ * @typedef {import("./flowNode.js").Data} Data
21
+ */
22
+
23
+ /**
24
+ * Current batch that is being collected.
25
+ * @type {Data}
26
+ */
27
+ #buffer = [];
28
+
29
+ #uniqueIdAccessor = field(UNIQUE_ID_KEY);
30
+
31
+ /**
32
+ * @type {number[]}
33
+ */
34
+ #uniqueIdIndex = [];
35
+
36
+ /**
37
+ * Start and end indices of all facets if they are concatenated into a single array.
38
+ * Used together with the uniqueIdIndex for looking up data items by their unique id.
39
+ * @type {{start: number, stop: number, facetId: import("../spec/channel.js").Scalar[]}[]}
40
+ */
41
+ #facetIndices;
42
+
16
43
  get behavior() {
17
44
  return BEHAVIOR_COLLECTS;
18
45
  }
@@ -28,32 +55,30 @@ export default class Collector extends FlowNode {
28
55
  /** @type {(function(Collector):void)[]} */
29
56
  this.observers = [];
30
57
 
31
- /** @type {Map<any | any[], import("./flowNode.js").Data>} TODO: proper type for key */
32
- this.facetBatches = undefined;
58
+ // TODO: Consider nested maps instead of InternMap
59
+ /** @type {Map<import("../spec/channel.js").Scalar[], Data>} TODO: proper type for key */
60
+ this.facetBatches = new InternMap([], JSON.stringify);
33
61
 
34
- this._init();
62
+ this.#init();
35
63
  }
36
64
 
37
- _init() {
38
- /** @type {import("./flowNode.js").Data} */
39
- this._data = [];
65
+ #init() {
66
+ this.#buffer = [];
40
67
 
41
- // TODO: Consider nested maps
42
- this.facetBatches = new InternMap([], JSON.stringify);
43
- this.facetBatches.set(undefined, this._data);
68
+ this.facetBatches.clear();
69
+ this.facetBatches.set(undefined, this.#buffer);
44
70
  }
45
71
 
46
72
  reset() {
47
73
  super.reset();
48
- this._init();
74
+ this.#init();
49
75
  }
50
76
 
51
77
  /**
52
- *
53
- * @param {import("./flowNode.js").Datum} datum
78
+ * @param {Datum} datum
54
79
  */
55
80
  handle(datum) {
56
- this._data.push(datum);
81
+ this.#buffer.push(datum);
57
82
  }
58
83
 
59
84
  /**
@@ -61,12 +86,15 @@ export default class Collector extends FlowNode {
61
86
  */
62
87
  beginBatch(flowBatch) {
63
88
  if (isFacetBatch(flowBatch)) {
64
- this._data = [];
65
- this.facetBatches.set(asArray(flowBatch.facetId), this._data);
89
+ this.#buffer = [];
90
+ this.facetBatches.set(asArray(flowBatch.facetId), this.#buffer);
66
91
  }
67
92
  }
68
93
 
69
94
  complete() {
95
+ // Free some memory
96
+ this.#buffer = [];
97
+
70
98
  const sort = this.params?.sort;
71
99
  // Vega's "compare" function is incredibly slow (uses megamorphic field accessor)
72
100
  // TODO: Implement a replacement for static data types
@@ -84,6 +112,8 @@ export default class Collector extends FlowNode {
84
112
  throw new Error("TODO: Support faceted data!");
85
113
  }
86
114
 
115
+ const data = this.facetBatches.get(undefined);
116
+
87
117
  const accessors = this.params.groupby.map((fieldName) =>
88
118
  field(fieldName)
89
119
  );
@@ -91,10 +121,10 @@ export default class Collector extends FlowNode {
91
121
  accessors.length > 1
92
122
  ? // There's something strange in d3-array's typings
93
123
  /** @type {Map<any, any>} */ /** @type {any} */ (
94
- group(this._data, ...accessors)
124
+ group(data, ...accessors)
95
125
  )
96
126
  : // D3's group is SLOW!
97
- groupBy(this._data, accessors[0]);
127
+ groupBy(data, accessors[0]);
98
128
 
99
129
  this.facetBatches.clear();
100
130
  for (const [key, data] of iterateNestedMaps(groups)) {
@@ -107,6 +137,7 @@ export default class Collector extends FlowNode {
107
137
  sortData(data);
108
138
  }
109
139
 
140
+ this.#buildUniqueIdIndex();
110
141
  this.#propagateToChildren();
111
142
 
112
143
  super.complete();
@@ -118,16 +149,16 @@ export default class Collector extends FlowNode {
118
149
 
119
150
  #propagateToChildren() {
120
151
  if (this.children.length) {
121
- for (const [key, data] of this.facetBatches.entries()) {
122
- if (key) {
152
+ for (const [facetId, data] of this.facetBatches.entries()) {
153
+ if (facetId) {
123
154
  /** @type {import("../types/flowBatch.js").FacetBatch} */
124
- const facetBatch = { type: "facet", facetId: key };
155
+ const facetBatch = { type: "facet", facetId };
125
156
  for (const child of this.children) {
126
157
  child.beginBatch(facetBatch);
127
158
  }
128
159
  }
129
- for (const datum of data) {
130
- this._propagate(datum);
160
+ for (let i = 0, n = data.length; i < n; i++) {
161
+ this._propagate(data[i]);
131
162
  }
132
163
  }
133
164
  }
@@ -146,10 +177,10 @@ export default class Collector extends FlowNode {
146
177
  }
147
178
 
148
179
  /**
149
- * @returns {Iterable<import("./flowNode.js").Datum>}
180
+ * @returns {Iterable<Datum>}
150
181
  */
151
182
  getData() {
152
- this._checkStatus();
183
+ this.#checkStatus();
153
184
 
154
185
  switch (this.facetBatches.size) {
155
186
  case 0:
@@ -161,9 +192,7 @@ export default class Collector extends FlowNode {
161
192
  return {
162
193
  [Symbol.iterator]: function* generator() {
163
194
  for (const data of groups.values()) {
164
- for (let i = 0; i < data.length; i++) {
165
- yield data[i];
166
- }
195
+ yield* data;
167
196
  }
168
197
  },
169
198
  };
@@ -173,10 +202,10 @@ export default class Collector extends FlowNode {
173
202
 
174
203
  /**
175
204
  *
176
- * @param {(datum: import("./flowNode.js").Datum) => void} visitor
205
+ * @param {(datum: Datum) => void} visitor
177
206
  */
178
207
  visitData(visitor) {
179
- this._checkStatus();
208
+ this.#checkStatus();
180
209
 
181
210
  for (const data of this.facetBatches.values()) {
182
211
  for (let i = 0; i < data.length; i++) {
@@ -196,21 +225,90 @@ export default class Collector extends FlowNode {
196
225
  return count;
197
226
  }
198
227
 
199
- _checkStatus() {
228
+ #checkStatus() {
200
229
  if (!this.completed) {
201
230
  throw new Error(
202
231
  "Data propagation is not completed! No data are available."
203
232
  );
204
233
  }
205
234
  }
235
+
236
+ /**
237
+ * Builds an index for looking up data items by their unique id.
238
+ * Using a sorted index and binary search for O(log n) complexity.
239
+ */
240
+ #buildUniqueIdIndex() {
241
+ this.#facetIndices = [];
242
+
243
+ /** @type {Datum} */
244
+ const obj = this.facetBatches.values().next().value?.[0];
245
+ if (obj == null || !(UNIQUE_ID_KEY in obj)) {
246
+ return; // No unique ids in the data
247
+ }
248
+
249
+ let cumulativePos = 0;
250
+
251
+ /** @type {number[]} */
252
+ const ids = [];
253
+
254
+ const a = this.#uniqueIdAccessor;
255
+
256
+ for (const [facetId, data] of this.facetBatches) {
257
+ this.#facetIndices.push({
258
+ start: cumulativePos,
259
+ stop: cumulativePos + data.length,
260
+ facetId,
261
+ });
262
+ cumulativePos += data.length;
263
+
264
+ for (let i = 0, n = data.length; i < n; i++) {
265
+ ids.push(a(data[i]));
266
+ }
267
+ }
268
+
269
+ this.#uniqueIdIndex = radixSortIntoLookupArray(ids);
270
+ }
271
+
272
+ /**
273
+ * Use an index to find a datum by its unique id.
274
+ *
275
+ * @param {number} uniqueId
276
+ */
277
+ findDatumByUniqueId(uniqueId) {
278
+ if (!this.#uniqueIdIndex.length) {
279
+ return;
280
+ }
281
+
282
+ const facetBisector = bisector((f) => f.start).right;
283
+ const a = this.#uniqueIdAccessor;
284
+ const indexBisector = bisector((i) => a(getDatum(i))).left;
285
+
286
+ const getDatum = (/** @type {number} */ i) => {
287
+ const fi = facetBisector(this.#facetIndices, i);
288
+ const facet = this.#facetIndices[fi - 1];
289
+ if (!facet || i >= facet.stop) {
290
+ return;
291
+ }
292
+ const data = this.facetBatches.get(facet.facetId);
293
+ return data[i - facet.start];
294
+ };
295
+
296
+ const index = indexBisector(this.#uniqueIdIndex, uniqueId);
297
+ if (index >= 0) {
298
+ const datum = getDatum(this.#uniqueIdIndex[index]);
299
+ if (datum && a(datum) === uniqueId) {
300
+ return datum;
301
+ }
302
+ }
303
+ }
206
304
  }
207
305
 
208
306
  /**
209
307
  * Like D3's group but without InternMap, which is slow.
210
308
  * TODO: Implement multi-level grouping
211
309
  *
212
- * @param {import("./flowNode.js").Datum[]} data
213
- * @param {(data: import("./flowNode.js").Datum) => import("../spec/channel.js").Scalar} accessor
310
+ * @param {Datum[]} data
311
+ * @param {(data: Datum) => import("../spec/channel.js").Scalar} accessor
214
312
  */
215
313
  function groupBy(data, accessor) {
216
314
  const groups = new Map();
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=collector.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"collector.test.d.ts","sourceRoot":"","sources":["../../../src/data/collector.test.js"],"names":[],"mappings":""}
@@ -1,6 +1,7 @@
1
- import { expect, test } from "vitest";
1
+ import { describe, expect, test } from "vitest";
2
2
 
3
3
  import Collector from "./collector.js";
4
+ import { UNIQUE_ID_KEY } from "./transforms/identifier.js";
4
5
 
5
6
  const data = [1, 5, 2, 4, 3].map((x) => ({ x }));
6
7
 
@@ -82,3 +83,56 @@ test("Collector throws on incomplete flow", () => {
82
83
 
83
84
  expect(() => collector.getData()).toThrow();
84
85
  });
86
+
87
+ describe("Indexing unique ids", () => {
88
+ test("Collector builds a working index when ids are available", () => {
89
+ const collector = new Collector({
90
+ type: "collect",
91
+ groupby: ["a"],
92
+ });
93
+
94
+ const data = [
95
+ { a: 1, x: 1, [UNIQUE_ID_KEY]: 8 },
96
+ { a: 1, x: 2, [UNIQUE_ID_KEY]: 2 },
97
+ { a: 1, x: 3, [UNIQUE_ID_KEY]: 4 },
98
+ { a: 1, x: 4, [UNIQUE_ID_KEY]: 6 },
99
+ { a: 2, x: 5, [UNIQUE_ID_KEY]: 9 },
100
+ { a: 2, x: 6, [UNIQUE_ID_KEY]: 7 },
101
+ { a: 2, x: 7, [UNIQUE_ID_KEY]: 3 },
102
+ { a: 2, x: 8, [UNIQUE_ID_KEY]: 1 },
103
+ ];
104
+
105
+ for (const d of data) {
106
+ collector.handle(d);
107
+ }
108
+ collector.complete();
109
+
110
+ expect(collector.findDatumByUniqueId(8)).toEqual(data[0]);
111
+ expect(collector.findDatumByUniqueId(2)).toEqual(data[1]);
112
+ expect(collector.findDatumByUniqueId(4)).toEqual(data[2]);
113
+ expect(collector.findDatumByUniqueId(6)).toEqual(data[3]);
114
+ expect(collector.findDatumByUniqueId(9)).toEqual(data[4]);
115
+ expect(collector.findDatumByUniqueId(7)).toEqual(data[5]);
116
+ expect(collector.findDatumByUniqueId(3)).toEqual(data[6]);
117
+ expect(collector.findDatumByUniqueId(1)).toEqual(data[7]);
118
+ });
119
+
120
+ test("Collector returns undefined when ids are not available", () => {
121
+ const collector = new Collector({
122
+ type: "collect",
123
+ groupby: ["a"],
124
+ });
125
+
126
+ const data = [
127
+ { a: 1, x: 1 },
128
+ { a: 2, x: 5 },
129
+ ];
130
+
131
+ for (const d of data) {
132
+ collector.handle(d);
133
+ }
134
+ collector.complete();
135
+
136
+ expect(collector.findDatumByUniqueId(0)).toBeUndefined();
137
+ });
138
+ });
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=dataFlow.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dataFlow.test.d.ts","sourceRoot":"","sources":["../../../src/data/dataFlow.test.js"],"names":[],"mappings":""}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=flow.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"flow.test.d.ts","sourceRoot":"","sources":["../../../src/data/flow.test.js"],"names":[],"mappings":""}
@@ -2,7 +2,10 @@ import { describe, expect, test } from "vitest";
2
2
  import FilterTransform from "./transforms/filter.js";
3
3
  import FormulaTransform from "./transforms/formula.js";
4
4
  import Collector from "./collector.js";
5
- import { SynchronousSequenceSource } from "./flowTestUtils.js";
5
+ import {
6
+ SynchronousSequenceSource,
7
+ makeParamMediatorProvider,
8
+ } from "./flowTestUtils.js";
6
9
 
7
10
  describe("Test flow graphs", () => {
8
11
  test("Trivial graph: sequence to collector", () => {
@@ -28,8 +31,6 @@ describe("Test flow graphs", () => {
28
31
 
29
32
  source.dispatch();
30
33
 
31
- expect(collector1.getData()).not.toBe(collector2._data);
32
-
33
34
  expect(collector1.getData()).toEqual(
34
35
  [0, 1, 2, 3, 4].map((d) => ({
35
36
  data: d,
@@ -45,18 +46,23 @@ describe("Test flow graphs", () => {
45
46
 
46
47
  test.skip("Implement stub for ParamMediator");
47
48
 
48
- /*
49
49
  test("Longer chain of nodes", () => {
50
50
  const source = new SynchronousSequenceSource(10);
51
- const filter = new FilterTransform({
52
- type: "filter",
53
- expr: "datum.data < 5",
54
- });
55
- const formula = new FormulaTransform({
56
- type: "formula",
57
- expr: "datum.data * 2",
58
- as: "data",
59
- });
51
+ const filter = new FilterTransform(
52
+ {
53
+ type: "filter",
54
+ expr: "datum.data < 5",
55
+ },
56
+ makeParamMediatorProvider()
57
+ );
58
+ const formula = new FormulaTransform(
59
+ {
60
+ type: "formula",
61
+ expr: "datum.data * 2",
62
+ as: "data",
63
+ },
64
+ makeParamMediatorProvider()
65
+ );
60
66
  const collector = new Collector();
61
67
 
62
68
  source.addChild(filter);
@@ -72,5 +78,4 @@ describe("Test flow graphs", () => {
72
78
  }))
73
79
  );
74
80
  });
75
- */
76
81
  });
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=flowNode.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"flowNode.test.d.ts","sourceRoot":"","sources":["../../../src/data/flowNode.test.js"],"names":[],"mappings":""}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=flowOptimizer.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"flowOptimizer.test.d.ts","sourceRoot":"","sources":["../../../src/data/flowOptimizer.test.js"],"names":[],"mappings":""}
@@ -10,6 +10,7 @@ import DataFlow from "./dataFlow.js";
10
10
  import { combineIdenticalDataSources } from "./flowOptimizer.js";
11
11
  import InlineSource from "./sources/inlineSource.js";
12
12
  import UrlSource from "./sources/urlSource.js";
13
+ import { makeParamMediatorProvider } from "./flowTestUtils.js";
13
14
 
14
15
  test("validateLinks() detects broken graph", () => {
15
16
  const root = new FlowNode();
@@ -132,14 +133,12 @@ describe("removeRedundantCloneTransforms", () => {
132
133
  });
133
134
  });
134
135
 
135
- const viewStub = {
136
- paramMediator: {
137
- registerParam: () => {},
138
- allocateSetter: () => {},
139
- createExpression: () => {},
140
- },
141
- getBaseUrl: () => "",
142
- };
136
+ /** @type {import("../view/view.js").default} */
137
+ const viewStub = /** @type {any} */ (
138
+ Object.assign(makeParamMediatorProvider(), {
139
+ getBaseUrl: () => "",
140
+ })
141
+ );
143
142
 
144
143
  describe("Merge indentical data sources", () => {
145
144
  test("Merges correctly", () => {
@@ -191,8 +190,8 @@ describe("Merge indentical data sources", () => {
191
190
  /** @type {DataFlow<string>} */
192
191
  const dataFlow = new DataFlow();
193
192
 
194
- const a = new InlineSource({ values: [1, 2, 3] });
195
- const b = new InlineSource({ values: [1, 2, 3] });
193
+ const a = new InlineSource({ values: [1, 2, 3] }, viewStub);
194
+ const b = new InlineSource({ values: [1, 2, 3] }, viewStub);
196
195
 
197
196
  dataFlow.addDataSource(a, "a");
198
197
  dataFlow.addDataSource(b, "b");
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=fasta.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fasta.test.d.ts","sourceRoot":"","sources":["../../../../src/data/formats/fasta.test.js"],"names":[],"mappings":""}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=inlineSource.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"inlineSource.test.d.ts","sourceRoot":"","sources":["../../../../src/data/sources/inlineSource.test.js"],"names":[],"mappings":""}
@@ -15,33 +15,37 @@ async function collectSource(source) {
15
15
  }
16
16
 
17
17
  test("InlineSource propagates an object", async () => {
18
- expect(await collectSource(new InlineSource({ values: { x: 1 } }))).toEqual(
19
- [{ x: 1 }]
20
- );
18
+ expect(
19
+ await collectSource(new InlineSource({ values: { x: 1 } }, undefined))
20
+ ).toEqual([{ x: 1 }]);
21
21
  });
22
22
 
23
23
  test("InlineSource propagates an array of objects", async () => {
24
24
  expect(
25
- await collectSource(new InlineSource({ values: [{ x: 1 }, { x: 2 }] }))
25
+ await collectSource(
26
+ new InlineSource({ values: [{ x: 1 }, { x: 2 }] }, undefined)
27
+ )
26
28
  ).toEqual([{ x: 1 }, { x: 2 }]);
27
29
  });
28
30
 
29
31
  test("InlineSource wraps scalars to objects", async () => {
30
- expect(await collectSource(new InlineSource({ values: [1, 2] }))).toEqual([
31
- { data: 1 },
32
- { data: 2 },
33
- ]);
32
+ expect(
33
+ await collectSource(new InlineSource({ values: [1, 2] }, undefined))
34
+ ).toEqual([{ data: 1 }, { data: 2 }]);
34
35
  });
35
36
 
36
37
  test("InlineSource parses a string", async () => {
37
38
  expect(
38
39
  await collectSource(
39
- new InlineSource({
40
- values: "a\n1\n2\n3",
41
- format: {
42
- type: "csv",
40
+ new InlineSource(
41
+ {
42
+ values: "a\n1\n2\n3",
43
+ format: {
44
+ type: "csv",
45
+ },
43
46
  },
44
- })
47
+ undefined
48
+ )
45
49
  )
46
50
  ).toEqual([{ a: 1 }, { a: 2 }, { a: 3 }]);
47
51
  });
@@ -49,8 +53,11 @@ test("InlineSource parses a string", async () => {
49
53
  test("InlineSource throws on a string and a missing format specifier", () => {
50
54
  expect(
51
55
  () =>
52
- new InlineSource({
53
- values: "a\n1\n2\n3",
54
- })
56
+ new InlineSource(
57
+ {
58
+ values: "a\n1\n2\n3",
59
+ },
60
+ undefined
61
+ )
55
62
  ).toThrow();
56
63
  });
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=sequenceSource.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sequenceSource.test.d.ts","sourceRoot":"","sources":["../../../../src/data/sources/sequenceSource.test.js"],"names":[],"mappings":""}