@genome-spy/core 0.41.0 → 0.42.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 (72) hide show
  1. package/dist/bundle/index--cKb-dKG.js +615 -0
  2. package/dist/bundle/{index-gn8bhQ8w.js → index-d7k3kkin.js} +365 -366
  3. package/dist/bundle/index.es.js +4077 -3900
  4. package/dist/bundle/index.js +111 -69
  5. package/dist/schema.json +58 -6
  6. package/dist/src/data/sources/dynamic/axisGenomeSource.js +1 -1
  7. package/dist/src/data/sources/dynamic/axisTickSource.js +3 -3
  8. package/dist/src/data/sources/dynamic/bamSource.d.ts +3 -21
  9. package/dist/src/data/sources/dynamic/bamSource.d.ts.map +1 -1
  10. package/dist/src/data/sources/dynamic/bamSource.js +38 -55
  11. package/dist/src/data/sources/dynamic/bigBedSource.d.ts +2 -38
  12. package/dist/src/data/sources/dynamic/bigBedSource.d.ts.map +1 -1
  13. package/dist/src/data/sources/dynamic/bigBedSource.js +14 -71
  14. package/dist/src/data/sources/dynamic/bigWigSource.d.ts +4 -42
  15. package/dist/src/data/sources/dynamic/bigWigSource.d.ts.map +1 -1
  16. package/dist/src/data/sources/dynamic/bigWigSource.js +23 -60
  17. package/dist/src/data/sources/dynamic/gff3Source.d.ts.map +1 -1
  18. package/dist/src/data/sources/dynamic/gff3Source.js +1 -0
  19. package/dist/src/data/sources/dynamic/indexedFastaSource.d.ts +2 -20
  20. package/dist/src/data/sources/dynamic/indexedFastaSource.d.ts.map +1 -1
  21. package/dist/src/data/sources/dynamic/indexedFastaSource.js +23 -41
  22. package/dist/src/data/sources/dynamic/singleAxisLazySource.d.ts +23 -4
  23. package/dist/src/data/sources/dynamic/singleAxisLazySource.d.ts.map +1 -1
  24. package/dist/src/data/sources/dynamic/singleAxisLazySource.js +29 -4
  25. package/dist/src/data/sources/dynamic/singleAxisWindowedSource.d.ts +60 -0
  26. package/dist/src/data/sources/dynamic/singleAxisWindowedSource.d.ts.map +1 -0
  27. package/dist/src/data/sources/dynamic/singleAxisWindowedSource.js +152 -0
  28. package/dist/src/data/sources/dynamic/tabixSource.d.ts +6 -40
  29. package/dist/src/data/sources/dynamic/tabixSource.d.ts.map +1 -1
  30. package/dist/src/data/sources/dynamic/tabixSource.js +29 -78
  31. package/dist/src/data/transforms/regexFold.d.ts.map +1 -1
  32. package/dist/src/data/transforms/regexFold.js +8 -0
  33. package/dist/src/data/transforms/regexFold.test.js +28 -0
  34. package/dist/src/genomeSpy.d.ts +14 -0
  35. package/dist/src/genomeSpy.d.ts.map +1 -1
  36. package/dist/src/genomeSpy.js +114 -8
  37. package/dist/src/gl/webGLHelper.d.ts +6 -21
  38. package/dist/src/gl/webGLHelper.d.ts.map +1 -1
  39. package/dist/src/gl/webGLHelper.js +7 -38
  40. package/dist/src/img/90-ring-with-bg.svg +1 -0
  41. package/dist/src/img/README.md +5 -0
  42. package/dist/src/marks/link.d.ts +7 -0
  43. package/dist/src/marks/link.d.ts.map +1 -1
  44. package/dist/src/marks/link.js +72 -37
  45. package/dist/src/marks/text.d.ts.map +1 -1
  46. package/dist/src/marks/text.js +16 -17
  47. package/dist/src/spec/data.d.ts +28 -13
  48. package/dist/src/spec/mark.d.ts +0 -8
  49. package/dist/src/styles/genome-spy.css.d.ts +1 -1
  50. package/dist/src/styles/genome-spy.css.d.ts.map +1 -1
  51. package/dist/src/styles/genome-spy.css.js +33 -4
  52. package/dist/src/styles/genome-spy.scss +40 -4
  53. package/dist/src/types/viewContext.d.ts +9 -0
  54. package/dist/src/utils/binnedIndex.d.ts +2 -0
  55. package/dist/src/utils/binnedIndex.d.ts.map +1 -1
  56. package/dist/src/utils/binnedIndex.js +59 -10
  57. package/dist/src/utils/binnedIndex.test.js +46 -0
  58. package/dist/src/view/gridView.d.ts.map +1 -1
  59. package/dist/src/view/gridView.js +2 -0
  60. package/dist/src/view/layerView.d.ts.map +1 -1
  61. package/dist/src/view/layerView.js +2 -0
  62. package/dist/src/view/unitView.d.ts +0 -6
  63. package/dist/src/view/unitView.d.ts.map +1 -1
  64. package/dist/src/view/unitView.js +2 -9
  65. package/dist/src/view/view.d.ts +6 -0
  66. package/dist/src/view/view.d.ts.map +1 -1
  67. package/dist/src/view/view.js +11 -0
  68. package/package.json +3 -3
  69. package/dist/bundle/index-Cbz74kpR.js +0 -638
  70. package/dist/src/data/sources/dynamic/windowedMixin.d.ts +0 -32
  71. package/dist/src/data/sources/dynamic/windowedMixin.d.ts.map +0 -1
  72. package/dist/src/data/sources/dynamic/windowedMixin.js +0 -53
@@ -1,4 +1,11 @@
1
1
  export default class LinkMark extends Mark {
2
+ /**
3
+ * Only available if "WebGL Draft Extensions" is enabled in chrome://flags
4
+ * But seems to work.
5
+ *
6
+ * @private
7
+ */
8
+ private _baseInstanceExt;
2
9
  arrays: {
3
10
  [k: string]: {
4
11
  data: any;
@@ -1 +1 @@
1
- {"version":3,"file":"link.d.ts","sourceRoot":"","sources":["../../../src/marks/link.js"],"names":[],"mappings":"AAWA;IA+HQ;;;;;;MAKC;CA2DR;iBArMgB,WAAW"}
1
+ {"version":3,"file":"link.d.ts","sourceRoot":"","sources":["../../../src/marks/link.js"],"names":[],"mappings":"AAWA;IA+BQ;;;;;OAKG;IACH,yBAEC;IAkGD;;;;;;MAKC;CAoFR;iBAxOgB,WAAW"}
@@ -1,4 +1,4 @@
1
- import { drawBufferInfo, setBuffersAndAttributes } from "twgl.js";
1
+ import { setBuffersAndAttributes } from "twgl.js";
2
2
  import VERTEX_SHADER from "../gl/link.vertex.glsl.js";
3
3
  import FRAGMENT_SHADER from "../gl/link.fragment.glsl.js";
4
4
  import { LinkVertexBuilder } from "../gl/dataToVertices.js";
@@ -39,6 +39,16 @@ export default class LinkMark extends Mark {
39
39
  orient: "vertical",
40
40
  })
41
41
  );
42
+
43
+ /**
44
+ * Only available if "WebGL Draft Extensions" is enabled in chrome://flags
45
+ * But seems to work.
46
+ *
47
+ * @private
48
+ */
49
+ this._baseInstanceExt = this.gl.getExtension(
50
+ "WEBGL_draw_instanced_base_vertex_base_instance"
51
+ );
42
52
  }
43
53
 
44
54
  getAttributes() {
@@ -154,6 +164,18 @@ export default class LinkMark extends Mark {
154
164
 
155
165
  ops.push(() => this.bindOrSetMarkUniformBlock());
156
166
 
167
+ if (this._baseInstanceExt) {
168
+ ops.push(() =>
169
+ setBuffersAndAttributes(
170
+ this.gl,
171
+ this.programInfo,
172
+ this.vertexArrayInfo
173
+ )
174
+ );
175
+ } else {
176
+ ops.push(() => this.gl.bindVertexArray(null));
177
+ }
178
+
157
179
  return ops;
158
180
  }
159
181
 
@@ -163,42 +185,55 @@ export default class LinkMark extends Mark {
163
185
  render(options) {
164
186
  const gl = this.gl;
165
187
 
166
- return this.createRenderCallback((offset, count) => {
167
- // We are using instanced drawing here.
168
- // However, WebGL does not provide glDrawArraysInstancedBaseInstance and thus,
169
- // we have to hack with offsets in vertexAttribPointer
170
- // TODO: Use VAOs more intelligently to reduce WebGL calls
171
- // TODO: Explore multiDrawArraysInstancedWEBGL
172
- // There's also a promising extension draft:
173
- // https://www.khronos.org/registry/webgl/extensions/WEBGL_draw_instanced_base_vertex_base_instance/
174
- // (and https://www.khronos.org/registry/webgl/extensions/WEBGL_multi_draw_instanced_base_vertex_base_instance/)
175
-
176
- this.gl.bindVertexArray(this.vertexArrayInfo.vertexArrayObject);
177
-
178
- for (const attribInfoObject of Object.entries(
179
- this.bufferInfo.attribs
180
- )) {
181
- const [attribute, attribInfo] = attribInfoObject;
182
- if (
183
- attribInfo.buffer &&
184
- attribInfo.numComponents &&
185
- attribInfo.divisor
186
- ) {
187
- attribInfo.offset =
188
- offset * this.arrays[attribute].numComponents * 4; // gl.FLOAT in bytes
189
- }
190
- }
191
- setBuffersAndAttributes(gl, this.programInfo, this.bufferInfo);
192
-
193
- drawBufferInfo(
194
- gl,
195
- this.bufferInfo,
196
- gl.TRIANGLE_STRIP,
197
- (this.properties.segments + 1) * 2, // number of vertices in a triangle strip
198
- 0,
199
- count
200
- );
201
- }, options);
188
+ const arcVertexCount = (this.properties.segments + 1) * 2;
189
+
190
+ return this._baseInstanceExt
191
+ ? this.createRenderCallback((offset, count) => {
192
+ // Using the following extension, which, however, is only a draft and
193
+ // available if "WebGL Draft Extensions" is enabled in chrome://flags
194
+ // https://www.khronos.org/registry/webgl/extensions/WEBGL_draw_instanced_base_vertex_base_instance/
195
+
196
+ this._baseInstanceExt.drawArraysInstancedBaseInstanceWEBGL(
197
+ gl.TRIANGLE_STRIP,
198
+ 0,
199
+ arcVertexCount,
200
+ count,
201
+ offset
202
+ );
203
+ }, options)
204
+ : this.createRenderCallback((offset, count) => {
205
+ // Because vanilla WebGL 2 does not provide glDrawArraysInstancedBaseInstance,
206
+ // we have to hack with offsets in vertexAttribPointer
207
+ //
208
+ // TODO: Use VAOs more intelligently to reduce WebGL calls. In other words,
209
+ // reserve one VAO for each facet/sample.
210
+
211
+ for (const attribInfoObject of Object.entries(
212
+ this.bufferInfo.attribs
213
+ )) {
214
+ const [attribute, attribInfo] = attribInfoObject;
215
+ if (
216
+ attribInfo.buffer &&
217
+ attribInfo.numComponents &&
218
+ attribInfo.divisor
219
+ ) {
220
+ attribInfo.offset =
221
+ offset * this.arrays[attribute].numComponents * 4; // gl.FLOAT in bytes
222
+ }
223
+ }
224
+ setBuffersAndAttributes(
225
+ gl,
226
+ this.programInfo,
227
+ this.bufferInfo
228
+ );
229
+
230
+ gl.drawArraysInstanced(
231
+ gl.TRIANGLE_STRIP,
232
+ 0,
233
+ arcVertexCount,
234
+ count
235
+ );
236
+ }, options);
202
237
  }
203
238
  }
204
239
 
@@ -1 +1 @@
1
- {"version":3,"file":"text.d.ts","sourceRoot":"","sources":["../../../src/marks/text.js"],"names":[],"mappings":"AAgCA;;;;;;;GAOG;AACH;IAsDQ,oDAMmD;CAiM1D;iBAxRgB,WAAW"}
1
+ {"version":3,"file":"text.d.ts","sourceRoot":"","sources":["../../../src/marks/text.js"],"names":[],"mappings":"AAgCA;;;;;;;GAOG;AACH;IAsDQ,oDAMmD;CAgM1D;iBAvRgB,WAAW"}
@@ -160,6 +160,22 @@ export default class TextMark extends Mark {
160
160
 
161
161
  const props = this.properties;
162
162
 
163
+ this.registerMarkUniform(
164
+ "uSdfNumerator",
165
+ /** @type {import("../spec/mark.js").ExprRef | number} */
166
+ ({ expr: "devicePixelRatio" }),
167
+ (dpr) => {
168
+ let q = 0.35; // TODO: Ensure that this makes sense. Now chosen by trial & error
169
+ if (this.properties.logoLetters) {
170
+ // Adjust to make stretched letters a bit less blurry
171
+ // A proper solution would probably be to compute gradients in the fragment shader
172
+ // to find a suitable divisor.
173
+ q /= 2;
174
+ }
175
+ return this.font.metrics.common.base / (dpr / q);
176
+ }
177
+ );
178
+
163
179
  // TODO: Use uniform block.
164
180
  setBlockUniforms(this.markUniformInfo, {
165
181
  uPaddingX: props.paddingX,
@@ -237,24 +253,7 @@ export default class TextMark extends Mark {
237
253
  prepareRender(options) {
238
254
  const ops = super.prepareRender(options);
239
255
 
240
- let q = 0.35; // TODO: Ensure that this makes sense. Now chosen by trial & error
241
- if (this.properties.logoLetters) {
242
- // Adjust to make stretched letters a bit less blurry
243
- // A proper solution would probably be to compute gradients in the fragment shader
244
- // to find a suitable divisor.
245
- q /= 2;
246
- }
247
- const uSdfNumerator =
248
- this.font.metrics.common.base /
249
- (this.unitView.context.devicePixelRatio / q);
250
-
251
256
  ops.push(() => {
252
- // TODO: only set if dpr changed
253
- setBlockUniforms(this.markUniformInfo, {
254
- uSdfNumerator,
255
- });
256
- this.markUniformsAltered = true;
257
-
258
257
  setUniforms(this.programInfo, {
259
258
  uTexture: this.font.texture,
260
259
  });
@@ -202,6 +202,29 @@ export type LazyDataParams =
202
202
  | BamData
203
203
  | Gff3Data;
204
204
 
205
+ export interface DebouncedData {
206
+ /**
207
+ * Debounce time for data updates, in milliseconds. Debouncing prevents
208
+ * excessive data updates when the user is zooming or panning around.
209
+ *
210
+ * __Default value:__ `200`
211
+ */
212
+ debounce?: number;
213
+
214
+ /**
215
+ * The debounce mode for data updates. If set to `"domain"`, domain change
216
+ * events (panning and zooming) will be debounced. If set to `"window"`,
217
+ * the data fetches initiated by the changes to the visible window (or tile)
218
+ * will be debounced. If your data is small, the `"window"` is better as
219
+ * it will start fetching data while the user is still panning around,
220
+ * resulting in a shorter perceived latency.
221
+ *
222
+ * __Default value:__ `"window"`
223
+ *
224
+ */
225
+ debounceMode?: "domain" | "window";
226
+ }
227
+
205
228
  export interface AxisTicksData {
206
229
  type: "axisTicks";
207
230
 
@@ -219,7 +242,7 @@ export interface AxisGenomeData {
219
242
  channel: PrimaryPositionalChannel;
220
243
  }
221
244
 
222
- export interface IndexedFastaData {
245
+ export interface IndexedFastaData extends DebouncedData {
223
246
  type: "indexedFasta";
224
247
 
225
248
  /**
@@ -250,7 +273,7 @@ export interface IndexedFastaData {
250
273
  windowSize?: number;
251
274
  }
252
275
 
253
- export interface BigWigData {
276
+ export interface BigWigData extends DebouncedData {
254
277
  type: "bigwig";
255
278
 
256
279
  /**
@@ -273,7 +296,7 @@ export interface BigWigData {
273
296
  pixelsPerBin?: number;
274
297
  }
275
298
 
276
- export interface BigBedData {
299
+ export interface BigBedData extends DebouncedData {
277
300
  type: "bigbed";
278
301
 
279
302
  /**
@@ -297,7 +320,7 @@ export interface BigBedData {
297
320
  windowSize?: number;
298
321
  }
299
322
 
300
- export interface BamData {
323
+ export interface BamData extends DebouncedData {
301
324
  type: "bam";
302
325
 
303
326
  /**
@@ -328,7 +351,7 @@ export interface BamData {
328
351
  windowSize?: number;
329
352
  }
330
353
 
331
- export interface TabixData {
354
+ export interface TabixData extends DebouncedData {
332
355
  /**
333
356
  * Which channel's scale domain to monitor.
334
357
  *
@@ -355,14 +378,6 @@ export interface TabixData {
355
378
  * __Default value:__ `30000000`
356
379
  */
357
380
  windowSize?: number;
358
-
359
- /**
360
- * The debounce time for domain changes, in milliseconds. Debouncing prevents
361
- * data fetches while the user is still panning around.
362
- *
363
- * __Default value:__ `200`
364
- */
365
- debounceDomainChange?: number;
366
381
  }
367
382
 
368
383
  export interface Gff3Data extends TabixData {
@@ -472,14 +472,6 @@ export interface MarkConfig
472
472
  * This property is intended for internal use.
473
473
  */
474
474
  minBufferSize?: number;
475
-
476
- /**
477
- * Builds and index for efficient rendering of subsets of the data.
478
- * The data must be sorted by the x coordinate.
479
- *
480
- * TODO: This should be enabled automatically if the data are sorted.
481
- */
482
- buildIndex?: boolean;
483
475
  }
484
476
 
485
477
  export interface MarkConfigAndType extends MarkConfig {
@@ -1,3 +1,3 @@
1
1
  export default css;
2
- declare const css: ".genome-spy {\n font-family: system-ui, \"Segoe UI\", Roboto, Helvetica, Arial, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\";\n position: relative;\n}\n.genome-spy canvas {\n transform: scale(1, 1);\n opacity: 1;\n transition: transform 0.6s, opacity 0.6s;\n}\n.genome-spy .loading-message {\n position: absolute;\n top: 0;\n bottom: 0;\n left: 0;\n right: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n.genome-spy .loading-message .message {\n color: #666;\n opacity: 0;\n transition: opacity 0.7s;\n}\n.genome-spy.loading canvas {\n transform: scale(0.95, 0.95);\n opacity: 0;\n}\n.genome-spy.loading .loading-message .message {\n opacity: 1;\n}\n.genome-spy.loading .ellipsis {\n animation: blinker 1s linear infinite;\n}\n@keyframes blinker {\n 50% {\n opacity: 0;\n }\n}\n.genome-spy .tooltip {\n position: absolute;\n max-width: 450px;\n overflow: hidden;\n background: #f6f6f6;\n padding: 10px;\n font-size: 13px;\n box-shadow: 0px 3px 15px 0px rgba(0, 0, 0, 0.21);\n pointer-events: none;\n z-index: 100;\n}\n.genome-spy .tooltip > :last-child {\n margin-bottom: 0;\n}\n.genome-spy .tooltip > .title {\n padding-bottom: 5px;\n margin-bottom: 5px;\n border-bottom: 1px dashed #b6b6b6;\n}\n.genome-spy .tooltip .summary {\n font-size: 12px;\n}\n.genome-spy .tooltip table {\n border-collapse: collapse;\n}\n.genome-spy .tooltip table:first-child {\n margin-top: 0;\n}\n.genome-spy .tooltip table th,\n.genome-spy .tooltip table td {\n padding: 2px 0.4em;\n vertical-align: top;\n}\n.genome-spy .tooltip table th:first-child,\n.genome-spy .tooltip table td:first-child {\n padding-left: 0;\n}\n.genome-spy .tooltip table th {\n text-align: left;\n font-weight: bold;\n}\n.genome-spy .tooltip .color-legend {\n display: inline-block;\n width: 0.8em;\n height: 0.8em;\n margin-left: 0.4em;\n box-shadow: 0px 0px 3px 1px white;\n}\n.genome-spy .tooltip .attributes .hovered {\n background-color: #e0e0e0;\n}\n.genome-spy .tooltip .na {\n color: #aaa;\n font-style: italic;\n font-size: 80%;\n}\n.genome-spy .gene-track-tooltip .summary {\n font-size: 90%;\n}\n.genome-spy .message-box {\n display: flex;\n align-items: center;\n justify-content: center;\n position: absolute;\n top: 0;\n height: 100%;\n width: 100%;\n}\n.genome-spy .message-box > div {\n border: 1px solid red;\n padding: 10px;\n background: #fff0f0;\n}";
2
+ declare const css: ".genome-spy {\n font-family: system-ui, \"Segoe UI\", Roboto, Helvetica, Arial, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\";\n position: relative;\n}\n.genome-spy canvas {\n transform: scale(1, 1);\n opacity: 1;\n transition: transform 0.6s, opacity 0.6s;\n}\n.genome-spy .loading-message {\n position: absolute;\n inset: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n.genome-spy .loading-message .message {\n color: #666;\n opacity: 0;\n transition: opacity 0.7s;\n}\n.genome-spy.loading canvas {\n transform: scale(0.95, 0.95);\n opacity: 0;\n}\n.genome-spy.loading .loading-message .message {\n opacity: 1;\n}\n.genome-spy.loading .ellipsis {\n animation: blinker 1s linear infinite;\n}\n@keyframes blinker {\n 50% {\n opacity: 0;\n }\n}\n.genome-spy .loading-indicators {\n position: absolute;\n inset: 0;\n user-select: none;\n pointer-events: none;\n}\n.genome-spy .loading-indicators div {\n position: absolute;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n.genome-spy .loading-indicators div > div {\n font-size: 11px;\n transition: opacity 0.2s;\n background: white;\n padding: 2px 5px;\n display: flex;\n border-radius: 3px;\n gap: 0.5em;\n opacity: 0;\n}\n.genome-spy .loading-indicators div > div.loading {\n opacity: 0.5;\n}\n.genome-spy .loading-indicators div > div > * {\n display: block;\n}\n.genome-spy .loading-indicators div > div img {\n width: 1.5em;\n height: 1.5em;\n}\n.genome-spy .tooltip {\n position: absolute;\n max-width: 450px;\n overflow: hidden;\n background: #f6f6f6;\n padding: 10px;\n font-size: 13px;\n box-shadow: 0px 3px 15px 0px rgba(0, 0, 0, 0.21);\n pointer-events: none;\n z-index: 100;\n}\n.genome-spy .tooltip > :last-child {\n margin-bottom: 0;\n}\n.genome-spy .tooltip > .title {\n padding-bottom: 5px;\n margin-bottom: 5px;\n border-bottom: 1px dashed #b6b6b6;\n}\n.genome-spy .tooltip .summary {\n font-size: 12px;\n}\n.genome-spy .tooltip table {\n border-collapse: collapse;\n}\n.genome-spy .tooltip table:first-child {\n margin-top: 0;\n}\n.genome-spy .tooltip table th,\n.genome-spy .tooltip table td {\n padding: 2px 0.4em;\n vertical-align: top;\n}\n.genome-spy .tooltip table th:first-child,\n.genome-spy .tooltip table td:first-child {\n padding-left: 0;\n}\n.genome-spy .tooltip table th {\n text-align: left;\n font-weight: bold;\n}\n.genome-spy .tooltip .color-legend {\n display: inline-block;\n width: 0.8em;\n height: 0.8em;\n margin-left: 0.4em;\n box-shadow: 0px 0px 3px 1px white;\n}\n.genome-spy .tooltip .attributes .hovered {\n background-color: #e0e0e0;\n}\n.genome-spy .tooltip .na {\n color: #aaa;\n font-style: italic;\n font-size: 80%;\n}\n.genome-spy .gene-track-tooltip .summary {\n font-size: 90%;\n}\n.genome-spy .message-box {\n display: flex;\n align-items: center;\n justify-content: center;\n position: absolute;\n top: 0;\n height: 100%;\n width: 100%;\n}\n.genome-spy .message-box > div {\n border: 1px solid red;\n padding: 10px;\n background: #fff0f0;\n}";
3
3
  //# sourceMappingURL=genome-spy.css.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"genome-spy.css.d.ts","sourceRoot":"","sources":["../../../src/styles/genome-spy.css.js"],"names":[],"mappings":";AAAA,q6EA+GG"}
1
+ {"version":3,"file":"genome-spy.css.d.ts","sourceRoot":"","sources":["../../../src/styles/genome-spy.css.js"],"names":[],"mappings":";AAAA,0jGA4IG"}
@@ -9,10 +9,7 @@ const css = `.genome-spy {
9
9
  }
10
10
  .genome-spy .loading-message {
11
11
  position: absolute;
12
- top: 0;
13
- bottom: 0;
14
- left: 0;
15
- right: 0;
12
+ inset: 0;
16
13
  display: flex;
17
14
  align-items: center;
18
15
  justify-content: center;
@@ -37,6 +34,38 @@ const css = `.genome-spy {
37
34
  opacity: 0;
38
35
  }
39
36
  }
37
+ .genome-spy .loading-indicators {
38
+ position: absolute;
39
+ inset: 0;
40
+ user-select: none;
41
+ pointer-events: none;
42
+ }
43
+ .genome-spy .loading-indicators div {
44
+ position: absolute;
45
+ display: flex;
46
+ align-items: center;
47
+ justify-content: center;
48
+ }
49
+ .genome-spy .loading-indicators div > div {
50
+ font-size: 11px;
51
+ transition: opacity 0.2s;
52
+ background: white;
53
+ padding: 2px 5px;
54
+ display: flex;
55
+ border-radius: 3px;
56
+ gap: 0.5em;
57
+ opacity: 0;
58
+ }
59
+ .genome-spy .loading-indicators div > div.loading {
60
+ opacity: 0.5;
61
+ }
62
+ .genome-spy .loading-indicators div > div > * {
63
+ display: block;
64
+ }
65
+ .genome-spy .loading-indicators div > div img {
66
+ width: 1.5em;
67
+ height: 1.5em;
68
+ }
40
69
  .genome-spy .tooltip {
41
70
  position: absolute;
42
71
  max-width: 450px;
@@ -18,10 +18,7 @@ $font-family: system-ui, "Segoe UI", Roboto, Helvetica, Arial, sans-serif,
18
18
 
19
19
  .loading-message {
20
20
  position: absolute;
21
- top: 0;
22
- bottom: 0;
23
- left: 0;
24
- right: 0;
21
+ inset: 0;
25
22
  display: flex;
26
23
 
27
24
  align-items: center;
@@ -55,6 +52,45 @@ $font-family: system-ui, "Segoe UI", Roboto, Helvetica, Arial, sans-serif,
55
52
  }
56
53
  }
57
54
 
55
+ .loading-indicators {
56
+ position: absolute;
57
+ inset: 0;
58
+
59
+ user-select: none;
60
+ pointer-events: none;
61
+
62
+ div {
63
+ position: absolute;
64
+ display: flex;
65
+ align-items: center;
66
+ justify-content: center;
67
+
68
+ > div {
69
+ font-size: 11px;
70
+ transition: opacity 0.2s;
71
+ background: white;
72
+ padding: 2px 5px;
73
+ display: flex;
74
+ border-radius: 3px;
75
+ gap: 0.5em;
76
+ opacity: 0;
77
+
78
+ &.loading {
79
+ opacity: 0.5;
80
+ }
81
+
82
+ > * {
83
+ display: block;
84
+ }
85
+
86
+ img {
87
+ width: 1.5em;
88
+ height: 1.5em;
89
+ }
90
+ }
91
+ }
92
+ }
93
+
58
94
  .tooltip {
59
95
  position: absolute;
60
96
 
@@ -70,6 +70,15 @@ export default interface ViewContext {
70
70
 
71
71
  getNamedDataFromProvider: (name: string) => any[];
72
72
 
73
+ /**
74
+ * Allows lazy data sources to signal that they are loading data.
75
+ * The status is shown in the UI somewhere within or near the view.
76
+ *
77
+ * @param view The view where the data source is located.
78
+ * @param status true if loading, false if not loading.
79
+ */
80
+ setDataLoadingStatus: (view: View, status: boolean) => void;
81
+
73
82
  /**
74
83
  * Returns true if the view is configured to be visible.
75
84
  * N.B. This does NOT consider ancestors' visibility.
@@ -9,6 +9,8 @@
9
9
  * A binned index for (overlapping) ranges that are sorted by their start position.
10
10
  * Each indexed range is associated with respective vertex indices.
11
11
  *
12
+ * The indexing scheme is somewhat similar to Tabix (https://academic.oup.com/bioinformatics/article/27/5/718/262743).
13
+ *
12
14
  * @param {number} size Number of bins
13
15
  * @param {[number, number]} domain Domain of positions
14
16
  * @param {(datum: T) => number} accessor Accessor for range's start position
@@ -1 +1 @@
1
- {"version":3,"file":"binnedIndex.d.ts","sourceRoot":"","sources":["../../../src/utils/binnedIndex.js"],"names":[],"mappings":"AAKA;;;;;;GAMG;AAEH;;;;;;;;;GASG;AACH,mDANW,MAAM,UACN,CAAC,MAAM,EAAE,MAAM,CAAC,0BACF,MAAM,4BACN,MAAM;iCAyChB,MAAM,kBACN,MAAM;;EAwGpB;6BA/JU,MAAM,OACN,MAAM,QACN,CAAC,MAAM,EAAE,MAAM,CAAC,KACd,CAAC,MAAM,EAAE,MAAM,CAAC"}
1
+ {"version":3,"file":"binnedIndex.d.ts","sourceRoot":"","sources":["../../../src/utils/binnedIndex.js"],"names":[],"mappings":"AAKA;;;;;;GAMG;AAEH;;;;;;;;;;;GAWG;AACH,mDANW,MAAM,UACN,CAAC,MAAM,EAAE,MAAM,CAAC,0BACF,MAAM,4BACN,MAAM;iCA2ChB,MAAM,kBACN,MAAM;oBAsHJ,MAAM;EA+BtB;6BAhNU,MAAM,OACN,MAAM,QACN,CAAC,MAAM,EAAE,MAAM,CAAC,KACd,CAAC,MAAM,EAAE,MAAM,CAAC"}
@@ -15,6 +15,8 @@ const MIN_INTEGER = -(2 ** 31);
15
15
  * A binned index for (overlapping) ranges that are sorted by their start position.
16
16
  * Each indexed range is associated with respective vertex indices.
17
17
  *
18
+ * The indexing scheme is somewhat similar to Tabix (https://academic.oup.com/bioinformatics/article/27/5/718/262743).
19
+ *
18
20
  * @param {number} size Number of bins
19
21
  * @param {[number, number]} domain Domain of positions
20
22
  * @param {(datum: T) => number} accessor Accessor for range's start position
@@ -27,13 +29,15 @@ export function createBinningRangeIndexer(
27
29
  accessor,
28
30
  accessor2 = accessor
29
31
  ) {
30
- const startIndices = new Int32Array(size);
32
+ const startIndices = new Array(size);
31
33
  startIndices.fill(MAX_INTEGER);
32
34
 
33
35
  let lastIndex = MIN_INTEGER;
36
+ let lastStart = -Infinity;
34
37
  let unordered = false;
35
38
 
36
- const endIndices = new Int32Array(size);
39
+ const endIndices = new Array(size);
40
+ endIndices.fill(0);
37
41
 
38
42
  const start = domain[0];
39
43
  const domainLength = domain[1] - domain[0];
@@ -63,17 +67,33 @@ export function createBinningRangeIndexer(
63
67
  * @param {number} endVertexIndex
64
68
  */
65
69
  function binningIndexer(datum, startVertexIndex, endVertexIndex) {
70
+ if (unordered) {
71
+ return;
72
+ }
73
+
66
74
  if (startVertexIndex > lastIndex) {
67
75
  lastIndex = startVertexIndex;
68
- } else if (!unordered) {
76
+ } else {
69
77
  unordered = true;
70
78
  // TODO: Contextual info like view path
71
79
  console.debug(
72
80
  "Items are not ordered properly. Disabling binned index."
73
81
  );
82
+ return;
74
83
  }
75
84
 
76
85
  const value = accessor(datum);
86
+
87
+ if (value < lastStart) {
88
+ unordered = true;
89
+ // TODO: Contextual info like view path
90
+ console.debug(
91
+ "Items are not ordered properly. Disabling binned index."
92
+ );
93
+ return;
94
+ }
95
+ lastStart = value;
96
+
77
97
  const bin = getBin(value, false);
78
98
 
79
99
  if (startIndices[bin] > startVertexIndex) {
@@ -93,18 +113,42 @@ export function createBinningRangeIndexer(
93
113
  * @param {number} endVertexIndex
94
114
  */
95
115
  function binningRangeIndexer(datum, startVertexIndex, endVertexIndex) {
116
+ if (unordered) {
117
+ return;
118
+ }
119
+
96
120
  if (startVertexIndex > lastIndex) {
97
121
  lastIndex = startVertexIndex;
98
- } else if (!unordered) {
122
+ } else {
99
123
  unordered = true;
100
124
  // TODO: Contextual info like view path
101
125
  console.debug(
102
- "Items are not ordered properly. Disabling binned index."
126
+ "Items (vertices) are not ordered properly. Disabling binned index."
103
127
  );
128
+ return;
104
129
  }
105
130
 
106
131
  const start = accessor(datum);
107
132
  const end = accessor2(datum);
133
+
134
+ if (start < lastStart) {
135
+ unordered = true;
136
+ // TODO: Contextual info like view path
137
+ console.debug(
138
+ "Items are not ordered properly. Disabling binned index."
139
+ );
140
+ return;
141
+ } else if (end < start) {
142
+ unordered = true;
143
+ // TODO: Contextual info like view path
144
+ console.debug(
145
+ "End index is less than start index. Disabling binned index. Datum: ",
146
+ datum
147
+ );
148
+ return;
149
+ }
150
+ lastStart = start;
151
+
108
152
  const startBin = getBin(start, false);
109
153
  const endBin = getBin(end, true);
110
154
 
@@ -135,7 +179,16 @@ export function createBinningRangeIndexer(
135
179
  return arr;
136
180
  };
137
181
 
182
+ /**
183
+ * Finalizes the index and returns a lookup function.
184
+ *
185
+ * @returns {Lookup}
186
+ */
138
187
  const getIndex = () => {
188
+ if (unordered) {
189
+ return undefined;
190
+ }
191
+
139
192
  for (let i = 1; i < endIndices.length; i++) {
140
193
  if (endIndices[i] < endIndices[i - 1]) {
141
194
  endIndices[i] = endIndices[i - 1];
@@ -159,9 +212,5 @@ export function createBinningRangeIndexer(
159
212
  binningIndexer.getIndex = getIndex;
160
213
  binningRangeIndexer.getIndex = getIndex;
161
214
 
162
- if (unordered) {
163
- return undefined;
164
- } else {
165
- return accessor == accessor2 ? binningIndexer : binningRangeIndexer;
166
- }
215
+ return accessor == accessor2 ? binningIndexer : binningRangeIndexer;
167
216
  }
@@ -152,4 +152,50 @@ describe("Binning Indexer", () => {
152
152
  expect(index(115, 116)).toEqual([4, 8]);
153
153
  expect(index(135, 145)).toEqual([6, 10]);
154
154
  });
155
+
156
+ test("Unordered ranges disable the index", () => {
157
+ const items = [
158
+ [10, 30],
159
+ [25, 50],
160
+
161
+ [112, 139],
162
+ [102, 129], // <- Unordered!
163
+ [121, 149],
164
+ ];
165
+ const indexer = createBinningRangeIndexer(
166
+ 100,
167
+ [0, 1000],
168
+ (x) => x[0],
169
+ (x) => x[1]
170
+ );
171
+
172
+ items.forEach((x, i) => indexer(x, i * 2, (i + 1) * 2));
173
+
174
+ const index = indexer.getIndex();
175
+
176
+ expect(index).toBeUndefined();
177
+ });
178
+
179
+ test("Inverted ranges disable the index", () => {
180
+ const items = [
181
+ [10, 30],
182
+ [25, 50],
183
+
184
+ [102, 129],
185
+ [139, 112], // <- Inverted!
186
+ [121, 149],
187
+ ];
188
+ const indexer = createBinningRangeIndexer(
189
+ 100,
190
+ [0, 1000],
191
+ (x) => x[0],
192
+ (x) => x[1]
193
+ );
194
+
195
+ items.forEach((x, i) => indexer(x, i * 2, (i + 1) * 2));
196
+
197
+ const index = indexer.getIndex();
198
+
199
+ expect(index).toBeUndefined();
200
+ });
155
201
  });
@@ -1 +1 @@
1
- {"version":3,"file":"gridView.d.ts","sourceRoot":"","sources":["../../../src/view/gridView.js"],"names":[],"mappings":"AAkwBA;;;GAGG;AACH,iDAHW,OAAO,iBAAiB,EAAE,cAAc,GACtC,OAAO,iBAAiB,EAAE,QAAQ,CAwB9C;AAED;;;GAGG;AACH,uDAHW,OAAO,iBAAiB,EAAE,cAAc,GACtC,OAAO,iBAAiB,EAAE,QAAQ,CA6C9C;AA2BD;;GAEG;AACH,8EAUC;AAED;;;;;GAKG;AACH,4CAJW,OAAO,uBAAuB,EAAE,OAAO,UACvC,OAAO,iBAAiB,EAAE,UAAU,YACpC,QAAQ,aAmBlB;AAz3BD;;;;;;;;;;;;;;;GAeG;AACH;IA6BI;;;;;;;;;OASG;IACH,kBARW,OAAO,iBAAiB,EAAE,aAAa,WACvC,OAAO,yBAAyB,EAAE,OAAO,gBACzC,aAAa,iDAEb,MAAM,WACN,MAAM,YACN,OAAO,WAAW,EAAE,WAAW,EAoBzC;IARG,8CAAgB;IAOhB,uBAA0B;IAG9B;;OAEG;IACH,qDAIC;IAeD;;OAEG;IACH,wDAKC;IAqBD;;OAEG;IACH,8CAEC;IAED,yBAEC;IAED;;OAEG;IACH,wCAmCC;IA2OD;;;;OAIG;IAEH,gBALW,OAAO,4CAA4C,EAAE,OAAO,UAC5D,OAAO,uBAAuB,EAAE,OAAO,YACvC,OAAO,uBAAuB,EAAE,gBAAgB,QA2O1D;;CAmGJ;AAgJD;IACI;;;;OAIG;IACH,6DAHW,aAAa,UACb,MAAM,EAoFhB;IAjFG,4BAAgC;IAChC,kCAAgB;IAChB,eAAoB;IAEpB,uBAAuB;IACvB,YADW,QAAQ,CACQ;IAE3B,uBAAuB;IACvB,kBADW,QAAQ,CACc;IAEjC,mFAAmF;IACnF,MADW,QAAQ,OAAO,OAAO,iBAAiB,EAAE,UAAU,EAAE,QAAQ,CAAC,CAAC,CAC5D;IAEd,4FAA4F;IAC5F,WADW,QAAQ,OAAO,OAAO,iBAAiB,EAAE,UAAU,EAAE,YAAY,CAAC,CAAC,CAC3D;IAEnB,0DAA0D;IAC1D,kEAAoB;IAEpB,uBAAuB;IACvB,OADW,QAAQ,CACG;IAEtB,wBAAwB;IACxB,QADW,SAAS,CACQ;IA4DhC,uEAcC;IAED;;OAEG;IACH,4BAkKC;IAED,uBAqBC;IAED,iCAEC;CACJ;qBA9qC0D,eAAe;sBAFpD,uBAAuB;0BAGnB,oBAAoB;qBAGzB,eAAe;yBALX,mBAAmB;AAirC5C;IACI;;;OAGG;IACH,uBAHW,SAAS,8CAyFnB;IAtDG;;;MAAoB;IACpB,2CAAsC;IAKtC,uBAAuB;IAEvB,wBAAwB;IACxB,2BAAqC;IA+CzC,0BAKC;IAWD;;;;OAIG;IACH,gCAHW,SAAS,UACT,SAAS,QA8CnB;IA7BG,0BAAsE;CA8B7E;oBAl1CmB,qBAAqB"}
1
+ {"version":3,"file":"gridView.d.ts","sourceRoot":"","sources":["../../../src/view/gridView.js"],"names":[],"mappings":"AAowBA;;;GAGG;AACH,iDAHW,OAAO,iBAAiB,EAAE,cAAc,GACtC,OAAO,iBAAiB,EAAE,QAAQ,CAwB9C;AAED;;;GAGG;AACH,uDAHW,OAAO,iBAAiB,EAAE,cAAc,GACtC,OAAO,iBAAiB,EAAE,QAAQ,CA6C9C;AA2BD;;GAEG;AACH,8EAUC;AAED;;;;;GAKG;AACH,4CAJW,OAAO,uBAAuB,EAAE,OAAO,UACvC,OAAO,iBAAiB,EAAE,UAAU,YACpC,QAAQ,aAmBlB;AA33BD;;;;;;;;;;;;;;;GAeG;AACH;IA6BI;;;;;;;;;OASG;IACH,kBARW,OAAO,iBAAiB,EAAE,aAAa,WACvC,OAAO,yBAAyB,EAAE,OAAO,gBACzC,aAAa,iDAEb,MAAM,WACN,MAAM,YACN,OAAO,WAAW,EAAE,WAAW,EAoBzC;IARG,8CAAgB;IAOhB,uBAA0B;IAG9B;;OAEG;IACH,qDAIC;IAeD;;OAEG;IACH,wDAKC;IAqBD;;OAEG;IACH,8CAEC;IAED,yBAEC;IAED;;OAEG;IACH,wCAmCC;IA2OD;;;;OAIG;IAEH,gBALW,OAAO,4CAA4C,EAAE,OAAO,UAC5D,OAAO,uBAAuB,EAAE,OAAO,YACvC,OAAO,uBAAuB,EAAE,gBAAgB,QA6O1D;;CAmGJ;AAgJD;IACI;;;;OAIG;IACH,6DAHW,aAAa,UACb,MAAM,EAoFhB;IAjFG,4BAAgC;IAChC,kCAAgB;IAChB,eAAoB;IAEpB,uBAAuB;IACvB,YADW,QAAQ,CACQ;IAE3B,uBAAuB;IACvB,kBADW,QAAQ,CACc;IAEjC,mFAAmF;IACnF,MADW,QAAQ,OAAO,OAAO,iBAAiB,EAAE,UAAU,EAAE,QAAQ,CAAC,CAAC,CAC5D;IAEd,4FAA4F;IAC5F,WADW,QAAQ,OAAO,OAAO,iBAAiB,EAAE,UAAU,EAAE,YAAY,CAAC,CAAC,CAC3D;IAEnB,0DAA0D;IAC1D,kEAAoB;IAEpB,uBAAuB;IACvB,OADW,QAAQ,CACG;IAEtB,wBAAwB;IACxB,QADW,SAAS,CACQ;IA4DhC,uEAcC;IAED;;OAEG;IACH,4BAkKC;IAED,uBAqBC;IAED,iCAEC;CACJ;qBAhrC0D,eAAe;sBAFpD,uBAAuB;0BAGnB,oBAAoB;qBAGzB,eAAe;yBALX,mBAAmB;AAmrC5C;IACI;;;OAGG;IACH,uBAHW,SAAS,8CAyFnB;IAtDG;;;MAAoB;IACpB,2CAAsC;IAKtC,uBAAuB;IAEvB,wBAAwB;IACxB,2BAAqC;IA+CzC,0BAKC;IAWD;;;;OAIG;IACH,gCAHW,SAAS,UACT,SAAS,QA8CnB;IA7BG,0BAAsE;CA8B7E;oBAp1CmB,qBAAqB"}