@genome-spy/core 0.14.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 (226) hide show
  1. package/dist/index.js +224 -0
  2. package/dist/style.css +1 -0
  3. package/package.json +54 -0
  4. package/src/data/collector.js +178 -0
  5. package/src/data/collector.test.js +82 -0
  6. package/src/data/dataFlow.js +109 -0
  7. package/src/data/dataFlow.test.js +3 -0
  8. package/src/data/facetNode.js +17 -0
  9. package/src/data/flow.test.js +71 -0
  10. package/src/data/flowBatch.d.ts +40 -0
  11. package/src/data/flowNode.js +283 -0
  12. package/src/data/flowNode.test.js +49 -0
  13. package/src/data/flowOptimizer.js +117 -0
  14. package/src/data/flowOptimizer.test.js +192 -0
  15. package/src/data/flowTestUtils.js +63 -0
  16. package/src/data/formats/fasta.js +32 -0
  17. package/src/data/formats/fasta.test.js +26 -0
  18. package/src/data/sources/dataSource.js +22 -0
  19. package/src/data/sources/dataSourceFactory.js +24 -0
  20. package/src/data/sources/dataUtils.js +31 -0
  21. package/src/data/sources/dynamicCallbackSource.js +56 -0
  22. package/src/data/sources/dynamicSource.js +36 -0
  23. package/src/data/sources/inlineSource.js +69 -0
  24. package/src/data/sources/inlineSource.test.js +55 -0
  25. package/src/data/sources/namedSource.js +74 -0
  26. package/src/data/sources/sequenceSource.js +46 -0
  27. package/src/data/sources/sequenceSource.test.js +45 -0
  28. package/src/data/sources/urlSource.js +74 -0
  29. package/src/data/transforms/aggregate.js +69 -0
  30. package/src/data/transforms/clone.js +40 -0
  31. package/src/data/transforms/clone.test.js +10 -0
  32. package/src/data/transforms/coverage.js +187 -0
  33. package/src/data/transforms/coverage.test.js +122 -0
  34. package/src/data/transforms/filter.js +37 -0
  35. package/src/data/transforms/filter.test.js +17 -0
  36. package/src/data/transforms/filterScoredLabels.js +134 -0
  37. package/src/data/transforms/flattenCompressedExons.js +57 -0
  38. package/src/data/transforms/flattenDelimited.js +68 -0
  39. package/src/data/transforms/flattenDelimited.test.js +86 -0
  40. package/src/data/transforms/flattenSequence.js +39 -0
  41. package/src/data/transforms/flattenSequence.test.js +33 -0
  42. package/src/data/transforms/formula.js +39 -0
  43. package/src/data/transforms/formula.test.js +18 -0
  44. package/src/data/transforms/identifier.js +108 -0
  45. package/src/data/transforms/identifier.test.js +82 -0
  46. package/src/data/transforms/linearizeGenomicCoordinate.js +101 -0
  47. package/src/data/transforms/measureText.js +44 -0
  48. package/src/data/transforms/pileup.js +128 -0
  49. package/src/data/transforms/pileup.test.js +69 -0
  50. package/src/data/transforms/project.js +41 -0
  51. package/src/data/transforms/project.test.js +31 -0
  52. package/src/data/transforms/regexExtract.js +61 -0
  53. package/src/data/transforms/regexExtract.test.js +66 -0
  54. package/src/data/transforms/regexFold.js +141 -0
  55. package/src/data/transforms/regexFold.test.js +159 -0
  56. package/src/data/transforms/sample.js +101 -0
  57. package/src/data/transforms/sample.test.js +37 -0
  58. package/src/data/transforms/stack.js +137 -0
  59. package/src/data/transforms/stack.test.js +90 -0
  60. package/src/data/transforms/transformFactory.js +60 -0
  61. package/src/encoder/accessor.js +82 -0
  62. package/src/encoder/accessor.test.js +46 -0
  63. package/src/encoder/encoder.js +369 -0
  64. package/src/encoder/encoder.test.js +97 -0
  65. package/src/fonts/Lato-Regular.json +1267 -0
  66. package/src/fonts/Lato-Regular.png +0 -0
  67. package/src/fonts/OFL.txt +93 -0
  68. package/src/fonts/README.md +3 -0
  69. package/src/fonts/bmFont.d.ts +58 -0
  70. package/src/fonts/bmFontManager.js +357 -0
  71. package/src/fonts/bmFontMetrics.js +108 -0
  72. package/src/genome/genome.js +305 -0
  73. package/src/genome/genome.test.js +152 -0
  74. package/src/genome/genomeStore.js +54 -0
  75. package/src/genome/locusFormat.js +31 -0
  76. package/src/genome/scaleIndex.js +199 -0
  77. package/src/genome/scaleIndex.test.js +61 -0
  78. package/src/genome/scaleLocus.js +112 -0
  79. package/src/genome/scaleLocus.test.js +3 -0
  80. package/src/genomeSpy.js +753 -0
  81. package/src/gl/arrayBuilder.js +199 -0
  82. package/src/gl/dataToVertices.js +621 -0
  83. package/src/gl/includes/common.glsl +63 -0
  84. package/src/gl/includes/fp64-arithmetic.glsl +187 -0
  85. package/src/gl/includes/fp64-utils.js +132 -0
  86. package/src/gl/includes/picking.fragment.glsl +3 -0
  87. package/src/gl/includes/picking.vertex.glsl +29 -0
  88. package/src/gl/includes/sampleFacet.glsl +107 -0
  89. package/src/gl/includes/scales.glsl +79 -0
  90. package/src/gl/includes/scales_fp64.glsl +30 -0
  91. package/src/gl/link.fragment.glsl +18 -0
  92. package/src/gl/link.vertex.glsl +111 -0
  93. package/src/gl/point.fragment.glsl +123 -0
  94. package/src/gl/point.vertex.glsl +128 -0
  95. package/src/gl/rect.fragment.glsl +51 -0
  96. package/src/gl/rect.vertex.glsl +114 -0
  97. package/src/gl/rule.fragment.glsl +52 -0
  98. package/src/gl/rule.vertex.glsl +89 -0
  99. package/src/gl/text.fragment.glsl +31 -0
  100. package/src/gl/text.vertex.glsl +246 -0
  101. package/src/gl/webGLHelper.js +490 -0
  102. package/src/img/bowtie.svg +1 -0
  103. package/src/img/genomespy-favicon.svg +34 -0
  104. package/src/index.html +11 -0
  105. package/src/index.js +151 -0
  106. package/src/marks/link.js +189 -0
  107. package/src/marks/mark.js +867 -0
  108. package/src/marks/markUtils.js +109 -0
  109. package/src/marks/pointMark.js +279 -0
  110. package/src/marks/rectMark.js +236 -0
  111. package/src/marks/rule.js +231 -0
  112. package/src/marks/text.js +274 -0
  113. package/src/options.d.ts +9 -0
  114. package/src/scale/colorUtils.js +184 -0
  115. package/src/scale/glslScaleGenerator.js +462 -0
  116. package/src/scale/scale.js +441 -0
  117. package/src/scale/scale.test.js +323 -0
  118. package/src/scale/ticks.js +198 -0
  119. package/src/scale/ticks.test.js +39 -0
  120. package/src/singlePageApp.js +13 -0
  121. package/src/spec/axis.d.ts +296 -0
  122. package/src/spec/channel.d.ts +127 -0
  123. package/src/spec/data.d.ts +185 -0
  124. package/src/spec/font.d.ts +15 -0
  125. package/src/spec/genome.d.ts +35 -0
  126. package/src/spec/mark.d.ts +432 -0
  127. package/src/spec/root.d.ts +22 -0
  128. package/src/spec/scale.d.ts +265 -0
  129. package/src/spec/tooltip.d.ts +9 -0
  130. package/src/spec/transform.d.ts +479 -0
  131. package/src/spec/view.d.ts +215 -0
  132. package/src/styles/genome-spy.scss +153 -0
  133. package/src/tooltip/dataTooltipHandler.js +59 -0
  134. package/src/tooltip/refseqGeneTooltipHandler.js +77 -0
  135. package/src/tooltip/tooltipHandler.ts +12 -0
  136. package/src/types/filetypes.d.ts +4 -0
  137. package/src/types/flatqueue.d.ts +53 -0
  138. package/src/types/glsl.d.ts +4 -0
  139. package/src/types/object.d.ts +21 -0
  140. package/src/types/vega-scale.d.ts +60 -0
  141. package/src/utils/animator.js +83 -0
  142. package/src/utils/arrayUtils.js +55 -0
  143. package/src/utils/binnedRangeIndex.js +83 -0
  144. package/src/utils/clamp.js +8 -0
  145. package/src/utils/cloner.js +32 -0
  146. package/src/utils/cloner.test.js +23 -0
  147. package/src/utils/coalesce.js +11 -0
  148. package/src/utils/coalesce.test.js +15 -0
  149. package/src/utils/concatIterables.js +26 -0
  150. package/src/utils/concatIterables.test.js +7 -0
  151. package/src/utils/debounce.js +37 -0
  152. package/src/utils/domainArray.js +224 -0
  153. package/src/utils/domainArray.test.js +129 -0
  154. package/src/utils/eerp.js +13 -0
  155. package/src/utils/expression.js +32 -0
  156. package/src/utils/field.js +28 -0
  157. package/src/utils/fisheye.js +60 -0
  158. package/src/utils/formatObject.js +31 -0
  159. package/src/utils/html.js +23 -0
  160. package/src/utils/html.test.js +13 -0
  161. package/src/utils/indexer.js +43 -0
  162. package/src/utils/indexer.test.js +46 -0
  163. package/src/utils/inertia.js +124 -0
  164. package/src/utils/interactionEvent.js +33 -0
  165. package/src/utils/iterateNestedMaps.js +21 -0
  166. package/src/utils/iterateNestedMaps.test.js +32 -0
  167. package/src/utils/kWayMerge.js +42 -0
  168. package/src/utils/kWayMerge.test.js +25 -0
  169. package/src/utils/layout/flexLayout.js +336 -0
  170. package/src/utils/layout/flexLayout.test.js +296 -0
  171. package/src/utils/layout/padding.js +107 -0
  172. package/src/utils/layout/point.js +23 -0
  173. package/src/utils/layout/rectangle.js +282 -0
  174. package/src/utils/layout/rectangle.test.js +171 -0
  175. package/src/utils/mergeObjects.js +99 -0
  176. package/src/utils/mergeObjects.test.js +41 -0
  177. package/src/utils/numberExtractor.js +24 -0
  178. package/src/utils/numberExtractor.test.js +5 -0
  179. package/src/utils/point.js +14 -0
  180. package/src/utils/propertyCacher.js +70 -0
  181. package/src/utils/propertyCacher.test.js +84 -0
  182. package/src/utils/propertyCoalescer.js +37 -0
  183. package/src/utils/propertyCoalescer.test.js +21 -0
  184. package/src/utils/reservationMap.js +103 -0
  185. package/src/utils/reservationMap.test.js +19 -0
  186. package/src/utils/scaleNull.js +19 -0
  187. package/src/utils/setOperations.js +75 -0
  188. package/src/utils/smoothstep.js +10 -0
  189. package/src/utils/throttle.js +34 -0
  190. package/src/utils/topK.js +76 -0
  191. package/src/utils/topK.test.js +63 -0
  192. package/src/utils/transition.js +74 -0
  193. package/src/utils/ui/tooltip.js +189 -0
  194. package/src/utils/url.js +22 -0
  195. package/src/utils/variableTools.js +24 -0
  196. package/src/utils/variableTools.test.js +12 -0
  197. package/src/view/axisResolution.js +135 -0
  198. package/src/view/axisResolution.test.js +200 -0
  199. package/src/view/axisView.js +746 -0
  200. package/src/view/channel.js +5 -0
  201. package/src/view/concatView.js +296 -0
  202. package/src/view/containerView.js +141 -0
  203. package/src/view/decoratorView.js +510 -0
  204. package/src/view/facetView.js +488 -0
  205. package/src/view/flowBuilder.js +362 -0
  206. package/src/view/flowBuilder.test.js +124 -0
  207. package/src/view/importView.js +19 -0
  208. package/src/view/layerView.js +60 -0
  209. package/src/view/rendering.d.ts +44 -0
  210. package/src/view/renderingContext/compositeViewRenderingContext.js +51 -0
  211. package/src/view/renderingContext/deferredViewRenderingContext.js +174 -0
  212. package/src/view/renderingContext/layoutRecorderViewRenderingContext.js +128 -0
  213. package/src/view/renderingContext/simpleViewRenderingContext.js +62 -0
  214. package/src/view/renderingContext/svgViewRenderingContext.js +121 -0
  215. package/src/view/renderingContext/viewRenderingContext.js +41 -0
  216. package/src/view/scaleResolution.js +756 -0
  217. package/src/view/scaleResolution.test.js +571 -0
  218. package/src/view/scaleResolutionApi.d.ts +40 -0
  219. package/src/view/testUtils.js +48 -0
  220. package/src/view/unitView.js +368 -0
  221. package/src/view/view.js +589 -0
  222. package/src/view/view.test.js +213 -0
  223. package/src/view/viewContext.d.ts +57 -0
  224. package/src/view/viewFactory.js +179 -0
  225. package/src/view/viewFactory.test.js +16 -0
  226. package/src/view/viewUtils.js +420 -0
@@ -0,0 +1,153 @@
1
+ @use "sass:math";
2
+
3
+ $basic-spacing: 10px;
4
+
5
+ $font-family: system-ui, "Segoe UI", Roboto, Helvetica, Arial, sans-serif,
6
+ "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
7
+
8
+ .genome-spy {
9
+ font-family: $font-family;
10
+
11
+ position: relative;
12
+
13
+ canvas {
14
+ transform: scale(1, 1);
15
+ opacity: 1;
16
+ transition: transform 0.6s, opacity 0.6s;
17
+ }
18
+
19
+ .loading-message {
20
+ position: absolute;
21
+ top: 0;
22
+ bottom: 0;
23
+ left: 0;
24
+ right: 0;
25
+ display: flex;
26
+
27
+ align-items: center;
28
+ justify-content: center;
29
+
30
+ .message {
31
+ color: #666;
32
+ opacity: 0;
33
+ transition: opacity 0.7s;
34
+ }
35
+ }
36
+
37
+ &.loading {
38
+ canvas {
39
+ transform: scale(0.95, 0.95);
40
+ opacity: 0;
41
+ }
42
+
43
+ .loading-message .message {
44
+ opacity: 1;
45
+ }
46
+
47
+ .ellipsis {
48
+ animation: blinker 1s linear infinite;
49
+ }
50
+
51
+ @keyframes blinker {
52
+ 50% {
53
+ opacity: 0;
54
+ }
55
+ }
56
+ }
57
+
58
+ .tooltip {
59
+ position: absolute;
60
+
61
+ max-width: 450px;
62
+ overflow: hidden;
63
+
64
+ $background-color: #f6f6f6;
65
+ background: $background-color;
66
+ padding: $basic-spacing;
67
+ font-size: 13px;
68
+
69
+ box-shadow: 0px 3px 15px 0px rgba(0, 0, 0, 0.21);
70
+
71
+ pointer-events: none;
72
+ z-index: 100;
73
+
74
+ > :last-child {
75
+ margin-bottom: 0;
76
+ }
77
+
78
+ > .title {
79
+ padding-bottom: math.div($basic-spacing, 2);
80
+ margin-bottom: math.div($basic-spacing, 2);
81
+ border-bottom: 1px dashed darken($background-color, 25%);
82
+ }
83
+
84
+ .summary {
85
+ font-size: 12px;
86
+ }
87
+
88
+ table {
89
+ &:first-child {
90
+ margin-top: 0;
91
+ }
92
+
93
+ border-collapse: collapse;
94
+
95
+ th,
96
+ td {
97
+ padding: 2px 0.4em;
98
+ vertical-align: top;
99
+
100
+ &:first-child {
101
+ padding-left: 0;
102
+ }
103
+ }
104
+
105
+ th {
106
+ text-align: left;
107
+ font-weight: bold;
108
+ }
109
+ }
110
+
111
+ .color-legend {
112
+ display: inline-block;
113
+ width: 0.8em;
114
+ height: 0.8em;
115
+ margin-left: 0.4em;
116
+ box-shadow: 0px 0px 3px 1px white;
117
+ }
118
+
119
+ .attributes {
120
+ .hovered {
121
+ background-color: #e0e0e0;
122
+ }
123
+ }
124
+
125
+ .na {
126
+ color: #aaa;
127
+ font-style: italic;
128
+ font-size: 80%;
129
+ }
130
+ }
131
+
132
+ .gene-track-tooltip {
133
+ .summary {
134
+ font-size: 90%;
135
+ }
136
+ }
137
+
138
+ .message-box {
139
+ display: flex;
140
+ align-items: center;
141
+ justify-content: center;
142
+ position: absolute;
143
+ top: 0;
144
+ height: 100%;
145
+ width: 100%;
146
+
147
+ > div {
148
+ border: 1px solid red;
149
+ padding: 10px;
150
+ background: #fff0f0;
151
+ }
152
+ }
153
+ }
@@ -0,0 +1,59 @@
1
+ import { html } from "lit-html";
2
+ import formatObject from "../utils/formatObject";
3
+
4
+ /**
5
+ * @type {import("./tooltipHandler").TooltipHandler}
6
+ */
7
+ export default async function dataTooltipHandler(datum, mark, params) {
8
+ /**
9
+ * @param {string} key
10
+ * @param {object} datum
11
+ */
12
+ const legend = (key, datum) => {
13
+ for (const [channel, encoder] of Object.entries(mark.encoders)) {
14
+ if (encoder?.accessor?.fields.includes(key)) {
15
+ switch (channel) {
16
+ case "color":
17
+ case "fill":
18
+ case "stroke":
19
+ return html`
20
+ <span
21
+ class="color-legend"
22
+ style=${`background-color: ${encoder(datum)}`}
23
+ ></span>
24
+ `;
25
+ default:
26
+ }
27
+ }
28
+ }
29
+
30
+ return "";
31
+ };
32
+
33
+ const table = html`
34
+ <table class="attributes">
35
+ ${Object.entries(datum)
36
+ .filter(([key, value]) => !key.startsWith("_"))
37
+ .map(
38
+ ([key, value]) => html`
39
+ <tr>
40
+ <th>${key}</th>
41
+ <td>
42
+ ${formatObject(value)} ${legend(key, datum)}
43
+ </td>
44
+ </tr>
45
+ `
46
+ )}
47
+ </table>
48
+ `;
49
+
50
+ const title = mark.unitView.spec.title
51
+ ? html`
52
+ <div class="title">
53
+ <strong>${mark.unitView.spec.title}</strong>
54
+ </div>
55
+ `
56
+ : "";
57
+
58
+ return html`${title}${table}`;
59
+ }
@@ -0,0 +1,77 @@
1
+ import { debounce } from "../utils/debounce";
2
+ import { html } from "lit-html";
3
+
4
+ /*
5
+ * https://www.ncbi.nlm.nih.gov/books/NBK25500/
6
+ *
7
+ * TODO: Implement tool & email parameters: https://www.ncbi.nlm.nih.gov/books/NBK25497/
8
+ */
9
+
10
+ // TODO: Replace with an LRU-cache
11
+ const symbolSummaryCache = new Map();
12
+
13
+ /**
14
+ * @type {import("./tooltipHandler").TooltipHandler}
15
+ */
16
+ export default async function refseqGeneTooltipHandler(datum, mark, params) {
17
+ const symbol = datum.symbol;
18
+
19
+ let summary =
20
+ symbolSummaryCache.get(symbol) ??
21
+ (await debouncedFetchGeneSummary(datum.symbol));
22
+
23
+ if (summary) {
24
+ symbolSummaryCache.set(symbol, summary);
25
+ return html`
26
+ <div class="title">
27
+ <strong>${summary.name}</strong>
28
+ ${summary.description}
29
+ </div>
30
+ <p class="summary">${summary.summary}</p>
31
+ <p class="source">Source: NCBI RefSeq Gene</p>
32
+ `;
33
+ } else {
34
+ return null;
35
+ }
36
+ }
37
+
38
+ /**
39
+ * @param {string} symbol
40
+ */
41
+ async function fetchGeneSummary(symbol) {
42
+ // TODO: Add more search terms to ensure that we really find genes specific to the current genome
43
+
44
+ console.log("Searching: " + symbol);
45
+
46
+ const opts = { mode: "cors" };
47
+
48
+ const searchResult = await fetch(
49
+ `https://eutils.ncbi.nlm.nih.gov/entrez/eutils/esearch.fcgi?db=gene&term=${symbol}[GENE]&sort=relevance&retmode=json`,
50
+ opts
51
+ ).then((res) => res.json());
52
+
53
+ // TODO: Handle failed searchs
54
+ const id = searchResult.esearchresult.idlist[0];
55
+
56
+ if (id) {
57
+ const summaryResult = await fetch(
58
+ `https://eutils.ncbi.nlm.nih.gov/entrez/eutils/esummary.fcgi?db=gene&id=${id}&retmode=json`,
59
+ opts
60
+ ).then((res) => res.json());
61
+
62
+ const summary = summaryResult.result[id];
63
+ return summary;
64
+ } else {
65
+ return null;
66
+ }
67
+ }
68
+
69
+ const debounced = debounce(fetchGeneSummary, 500);
70
+
71
+ /**
72
+ *
73
+ * @param {string} symbol
74
+ */
75
+ function debouncedFetchGeneSummary(symbol) {
76
+ return debounced(symbol);
77
+ }
@@ -0,0 +1,12 @@
1
+ import { TemplateResult } from "lit-html";
2
+ import Mark from "../marks/mark";
3
+
4
+ /**
5
+ * Converts a datum to tooltip (HTMLElement or lit's TemplateResult).
6
+ */
7
+ export type TooltipHandler = (
8
+ datum: Record<string, any>,
9
+ mark: Mark,
10
+ /** Optional parameters from the view specification */
11
+ params?: Record<string, any>
12
+ ) => Promise<string | TemplateResult | HTMLElement>;
@@ -0,0 +1,4 @@
1
+ declare module "*.svg" {
2
+ const content: string;
3
+ export default content;
4
+ }
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Adapted from: https://github.com/mourner/flatqueue/blob/master/index.d.ts
3
+ *
4
+ * A version with typings is yet be released...
5
+ */
6
+ declare module "flatqueue" {
7
+ export default class FlatQueue<T> {
8
+ /**
9
+ * Number of items in the queue.
10
+ */
11
+ readonly length: number;
12
+
13
+ constructor();
14
+
15
+ /**
16
+ * Removes all items from the queue.
17
+ */
18
+ clear(): void;
19
+
20
+ /**
21
+ * Adds `item` to the queue with the specified `priority`.
22
+ *
23
+ * `priority` must be a number. Items are sorted and returned from low to
24
+ * high priority. Multiple items with the same priority value can be added
25
+ * to the queue, but there is no guaranteed order between these items.
26
+ */
27
+ push(item: T, priority: number): void;
28
+
29
+ /**
30
+ * Removes and returns the item from the head of this queue, which is one of
31
+ * the items with the lowest priority. If this queue is empty, returns
32
+ * `undefined`.
33
+ */
34
+ pop(): T | undefined;
35
+
36
+ /**
37
+ * Returns the item from the head of this queue without removing it. If this
38
+ * queue is empty, returns `undefined`.
39
+ */
40
+ peek(): T | undefined;
41
+
42
+ /**
43
+ * Returns the priority value of the item at the head of this queue without
44
+ * removing it. If this queue is empty, returns `undefined`.
45
+ */
46
+ peekValue(): number | undefined;
47
+
48
+ /**
49
+ * Shrinks the internal arrays to `this.length`.
50
+ */
51
+ shrink(): void;
52
+ }
53
+ }
@@ -0,0 +1,4 @@
1
+ declare module "*.glsl" {
2
+ const glsl: string;
3
+ export default glsl;
4
+ }
@@ -0,0 +1,21 @@
1
+ type ObjectKeys<T> = T extends object
2
+ ? (keyof T)[]
3
+ : T extends number
4
+ ? []
5
+ : T extends Array<any> | string
6
+ ? string[]
7
+ : never;
8
+
9
+ interface ObjectConstructor {
10
+ // Source: https://fettblog.eu/typescript-better-object-keys/
11
+ keys<T>(o: T): ObjectKeys<T>;
12
+
13
+ // Source: https://github.com/microsoft/TypeScript/issues/35101
14
+ entries<T>(
15
+ o: T
16
+ ): T extends ArrayLike<infer U>
17
+ ? [string, U][]
18
+ : { [K in keyof T]: [K, T[K]] }[keyof T][];
19
+
20
+ values<T>(o: T): T extends ArrayLike<infer U> ? U[] : T[keyof T][];
21
+ }
@@ -0,0 +1,60 @@
1
+ type MetaDatum =
2
+ | "continuous"
3
+ | "discrete"
4
+ | "discretizing"
5
+ | "interpolating"
6
+ | "log"
7
+ | "temporal";
8
+
9
+ type Color = string;
10
+ type Interpolator<T> = (t: number) => T;
11
+ type ColorInterpolator = Interpolator<Color>;
12
+
13
+ declare module "vega-scale" {
14
+ // TODO: Correct return type
15
+ export function scale(
16
+ type: string,
17
+ scale: () => any,
18
+ metadata: MetaDatum | MetaDatum[]
19
+ ): any;
20
+
21
+ export function scheme(
22
+ name: string,
23
+ scheme: string[] | ColorInterpolator
24
+ ): void;
25
+ export function scheme(name: string): ColorInterpolator;
26
+
27
+ // TODO: Correct return type
28
+ export function interpolate(name: string, gamma?: number): any;
29
+
30
+ export function interpolateColors(
31
+ colors: Color[],
32
+ type?: string,
33
+ gamma?: number
34
+ ): ColorInterpolator;
35
+
36
+ export function interpolateRange<T>(
37
+ interpolator: Interpolator<T>,
38
+ range: number[]
39
+ ): Interpolator<T>;
40
+
41
+ export function quantizeInterpolator<T>(
42
+ interpolator: Interpolator<T>,
43
+ count: number
44
+ ): T[];
45
+
46
+ export function isValidScaleType(type: string): boolean;
47
+ export function isContinuous(key: string): boolean;
48
+ export function isDiscrete(key: string): boolean;
49
+ export function isDiscretizing(key: string): boolean;
50
+ export function isLogarithmic(key: string): boolean;
51
+ export function isTemporal(key: string): boolean;
52
+ export function isInterpolating(key: string): boolean;
53
+ export function isQuantile(key: string): boolean;
54
+
55
+ export function bandSpace(
56
+ count: number,
57
+ paddingInner: number,
58
+ paddingOuter: number
59
+ ): number;
60
+ }
@@ -0,0 +1,83 @@
1
+ import doTransition from "./transition";
2
+
3
+ export default class Animator {
4
+ /**
5
+ *
6
+ * @param {function(number):void} renderCallback
7
+ */
8
+ constructor(renderCallback) {
9
+ this._renderCallback = renderCallback;
10
+ this._renderRequested = false;
11
+ this._warn = false;
12
+
13
+ /** @type {(function(number):void)[]} */
14
+ this.transitions = [];
15
+ }
16
+
17
+ /**
18
+ * Schedules a "transition" to be called before the actual rendering
19
+ * is preformed. The transition could adjust the layout, for example.
20
+ * This method also requests rendering to be performed.
21
+ *
22
+ * If the callback has already been requested (compared by identity),
23
+ * it is removed from the queue and added to the end.
24
+ *
25
+ * @param {function(number):void} callback
26
+ */
27
+ requestTransition(callback) {
28
+ this.cancelTransition(callback);
29
+ this.transitions.push(callback);
30
+ this.requestRender();
31
+ }
32
+
33
+ /**
34
+ * @param {function(number):void} callback
35
+ */
36
+ cancelTransition(callback) {
37
+ const existingIndex = this.transitions.indexOf(callback);
38
+ if (existingIndex >= 0) {
39
+ this.transitions.splice(existingIndex, 1);
40
+ }
41
+ }
42
+
43
+ /**
44
+ * Requests the request transitions and rendering callback to be called
45
+ * during the next animation frame. Redundant calls to this method are safe,
46
+ * they have no effect.
47
+ */
48
+ requestRender() {
49
+ if (!this._renderRequested) {
50
+ this._renderRequested = true;
51
+ window.requestAnimationFrame((timestamp) => {
52
+ this._renderRequested = false;
53
+
54
+ const transitions = this.transitions;
55
+ this.transitions = [];
56
+
57
+ /** @type {function} */
58
+ let transitionCallback;
59
+ while ((transitionCallback = transitions.shift())) {
60
+ transitionCallback(timestamp);
61
+ }
62
+
63
+ this._renderCallback(timestamp);
64
+ });
65
+ } else if (this._warn) {
66
+ console.warn("Render already requested!");
67
+ }
68
+ }
69
+
70
+ /**
71
+ * Initiates a transition with a `requestAnimationFrame` that is synced
72
+ * with this Animator instance.
73
+ *
74
+ * @param {import("./transition").TransitionOptions} options
75
+ */
76
+ transition(options) {
77
+ return doTransition({
78
+ requestAnimationFrame: (callback) =>
79
+ this.requestTransition(callback),
80
+ ...options,
81
+ });
82
+ }
83
+ }
@@ -0,0 +1,55 @@
1
+ /**
2
+ *
3
+ * @param {A[]} a
4
+ * @param {B[]} b
5
+ * @param {function(A):T} [aAccessor]
6
+ * @param {function(B):T} [bAccessor]
7
+ * @template A, B, T
8
+ */
9
+ export function shallowArrayEquals(a, b, aAccessor, bAccessor) {
10
+ aAccessor = aAccessor || ((x) => x);
11
+ bAccessor = bAccessor || ((x) => x);
12
+ return (
13
+ a.length == b.length &&
14
+ a.every((s, i) => aAccessor(a[i]) === bAccessor(b[i]))
15
+ );
16
+ }
17
+
18
+ /**
19
+ *
20
+ * @param {any[]} a
21
+ */
22
+ export function isHomogeneous(a) {
23
+ if (a.length <= 1) {
24
+ return true;
25
+ }
26
+
27
+ const first = a[0];
28
+ return a.every((x) => x === first);
29
+ }
30
+
31
+ /**
32
+ * @param {T[] | T} obj
33
+ * @returns {T[]}
34
+ * @template T
35
+ */
36
+ export function asArray(obj) {
37
+ if (Array.isArray(obj)) {
38
+ return obj;
39
+ } else if (typeof obj != "undefined") {
40
+ return [obj];
41
+ } else {
42
+ return [];
43
+ }
44
+ }
45
+
46
+ /**
47
+ * Returns the last element of an array.
48
+ * Like vega-util's peek but with stricter typings
49
+ *
50
+ * @param {T[]} arr
51
+ * @template T
52
+ */
53
+ export function peek(arr) {
54
+ return arr[arr.length - 1];
55
+ }
@@ -0,0 +1,83 @@
1
+ import clamp from "./clamp";
2
+
3
+ const MAX_INTEGER = 2 ** 31 - 1;
4
+
5
+ /**
6
+ * @callback Lookup
7
+ * @param {number} start
8
+ * @param {number} end
9
+ * @returns {[number, number]}
10
+ */
11
+
12
+ /**
13
+ * A binned index for (overlapping) ranges that are sorted by their start position.
14
+ * Allows for indexing vertices of mark instances.
15
+ *
16
+ * @param {number} size Number of bins
17
+ * @param {[number, number]} domain
18
+ */
19
+ export default function createBinningRangeIndexer(size, domain) {
20
+ const startIndices = new Int32Array(size);
21
+ startIndices.fill(MAX_INTEGER);
22
+
23
+ const endIndices = new Int32Array(size);
24
+
25
+ const start = domain[0];
26
+ const domainLength = domain[1] - domain[0];
27
+ const divisor = domainLength / size;
28
+
29
+ /** @param {number} pos */
30
+ const getBin = (pos) =>
31
+ clamp(Math.floor((pos - start) / divisor), 0, size - 1);
32
+
33
+ /**
34
+ *
35
+ * @param {number} start
36
+ * @param {number} end
37
+ * @param {number} startIndex
38
+ * @param {number} endIndex
39
+ */
40
+ const indexer = (start, end, startIndex, endIndex) => {
41
+ const startBin = getBin(start);
42
+ const endBin = getBin(end);
43
+
44
+ // TODO: This loop could probably be done as a more efficient post processing
45
+ // step.
46
+ for (let bin = startBin; bin <= endBin; bin++) {
47
+ if (startIndices[bin] > startIndex) {
48
+ startIndices[bin] = startIndex;
49
+ }
50
+
51
+ if (endIndices[bin] < endIndex) {
52
+ endIndices[bin] = endIndex;
53
+ }
54
+ }
55
+ };
56
+
57
+ /**
58
+ * @type {Lookup}
59
+ */
60
+ const lookup = (start, end) => [
61
+ startIndices[getBin(start)],
62
+ endIndices[getBin(end)],
63
+ ];
64
+
65
+ const getIndex = () => {
66
+ for (let i = 1; i < endIndices.length; i++) {
67
+ if (endIndices[i] < endIndices[i - 1]) {
68
+ endIndices[i] = endIndices[i - 1];
69
+ }
70
+ }
71
+ for (let i = endIndices.length - 1; i > 0; i--) {
72
+ if (endIndices[i - 1] > endIndices[i]) {
73
+ endIndices[i - 1] = endIndices[i];
74
+ }
75
+ }
76
+
77
+ return lookup;
78
+ };
79
+
80
+ indexer.getIndex = getIndex;
81
+
82
+ return indexer;
83
+ }
@@ -0,0 +1,8 @@
1
+ /**
2
+ * @param {number} value
3
+ * @param {number} [min]
4
+ * @param {number} [max]
5
+ */
6
+ export default function clamp(value, min = 0, max = 1) {
7
+ return Math.max(min, Math.min(max, value));
8
+ }