@genome-spy/core 0.72.0 → 0.73.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +1 -1
- package/dist/bundle/index.es.js +6779 -5393
- package/dist/bundle/index.js +133 -121
- package/dist/schema.json +281 -17
- package/dist/src/data/formats/bed.d.ts +8 -0
- package/dist/src/data/formats/bed.d.ts.map +1 -0
- package/dist/src/data/formats/bed.js +53 -0
- package/dist/src/data/formats/bedpe.d.ts +8 -0
- package/dist/src/data/formats/bedpe.d.ts.map +1 -0
- package/dist/src/data/formats/bedpe.js +160 -0
- package/dist/src/data/sources/dataUtils.d.ts +16 -0
- package/dist/src/data/sources/dataUtils.d.ts.map +1 -1
- package/dist/src/data/sources/dataUtils.js +53 -3
- package/dist/src/data/sources/urlSource.d.ts +4 -0
- package/dist/src/data/sources/urlSource.d.ts.map +1 -1
- package/dist/src/data/sources/urlSource.js +133 -14
- package/dist/src/genome/assemblyPreflight.d.ts +31 -0
- package/dist/src/genome/assemblyPreflight.d.ts.map +1 -0
- package/dist/src/genome/assemblyPreflight.js +99 -0
- package/dist/src/genome/genome.d.ts +2 -2
- package/dist/src/genome/genome.d.ts.map +1 -1
- package/dist/src/genome/genome.js +4 -0
- package/dist/src/genome/genomeStore.d.ts +34 -3
- package/dist/src/genome/genomeStore.d.ts.map +1 -1
- package/dist/src/genome/genomeStore.js +409 -18
- package/dist/src/genome/rootGenomeConfig.d.ts +26 -0
- package/dist/src/genome/rootGenomeConfig.d.ts.map +1 -0
- package/dist/src/genome/rootGenomeConfig.js +94 -0
- package/dist/src/genomeSpy/interactionController.d.ts +5 -1
- package/dist/src/genomeSpy/interactionController.d.ts.map +1 -1
- package/dist/src/genomeSpy/interactionController.js +244 -29
- package/dist/src/genomeSpy/renderCoordinator.js +1 -1
- package/dist/src/genomeSpy.d.ts +13 -3
- package/dist/src/genomeSpy.d.ts.map +1 -1
- package/dist/src/genomeSpy.js +81 -7
- package/dist/src/gl/canvasSizeHelper.d.ts +74 -0
- package/dist/src/gl/canvasSizeHelper.d.ts.map +1 -0
- package/dist/src/gl/canvasSizeHelper.js +203 -0
- package/dist/src/gl/webGLHelper.d.ts +25 -11
- package/dist/src/gl/webGLHelper.d.ts.map +1 -1
- package/dist/src/gl/webGLHelper.js +59 -33
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +5 -2
- package/dist/src/marks/link.d.ts.map +1 -1
- package/dist/src/marks/link.js +5 -3
- package/dist/src/marks/mark.d.ts.map +1 -1
- package/dist/src/marks/mark.js +6 -1
- package/dist/src/scales/domainPlanner.d.ts +34 -3
- package/dist/src/scales/domainPlanner.d.ts.map +1 -1
- package/dist/src/scales/domainPlanner.js +247 -26
- package/dist/src/scales/scaleInstanceManager.d.ts +2 -1
- package/dist/src/scales/scaleInstanceManager.d.ts.map +1 -1
- package/dist/src/scales/scaleInstanceManager.js +10 -11
- package/dist/src/scales/scaleInteractionController.d.ts.map +1 -1
- package/dist/src/scales/scaleInteractionController.js +16 -14
- package/dist/src/scales/scaleResolution.d.ts +16 -0
- package/dist/src/scales/scaleResolution.d.ts.map +1 -1
- package/dist/src/scales/scaleResolution.js +314 -54
- package/dist/src/scales/scaleResolutionTestUtils.d.ts +21 -0
- package/dist/src/scales/scaleResolutionTestUtils.d.ts.map +1 -0
- package/dist/src/scales/scaleResolutionTestUtils.js +33 -0
- package/dist/src/scales/selectionDomainUtils.d.ts +22 -0
- package/dist/src/scales/selectionDomainUtils.d.ts.map +1 -0
- package/dist/src/scales/selectionDomainUtils.js +79 -0
- package/dist/src/scales/zoomDomainUtils.d.ts +18 -0
- package/dist/src/scales/zoomDomainUtils.d.ts.map +1 -0
- package/dist/src/scales/zoomDomainUtils.js +69 -0
- package/dist/src/screenshotHarness.d.ts +16 -0
- package/dist/src/screenshotHarness.d.ts.map +1 -0
- package/dist/src/screenshotHarness.js +242 -0
- package/dist/src/singlePageApp.js +1 -1
- package/dist/src/spec/data.d.ts +23 -3
- package/dist/src/spec/genome.d.ts +22 -2
- package/dist/src/spec/parameter.d.ts +39 -2
- package/dist/src/spec/root.d.ts +20 -1
- package/dist/src/spec/scale.d.ts +41 -5
- package/dist/src/styles/genome-spy.css +8 -0
- package/dist/src/styles/genome-spy.css.d.ts +1 -1
- package/dist/src/styles/genome-spy.css.d.ts.map +1 -1
- package/dist/src/styles/genome-spy.css.js +8 -0
- package/dist/src/tooltip/dataTooltipHandler.js +59 -10
- package/dist/src/types/embedApi.d.ts +19 -0
- package/dist/src/utils/inferSpecBaseUrl.d.ts +14 -0
- package/dist/src/utils/inferSpecBaseUrl.d.ts.map +1 -0
- package/dist/src/utils/inferSpecBaseUrl.js +73 -0
- package/dist/src/utils/interactionEvent.d.ts +53 -3
- package/dist/src/utils/interactionEvent.d.ts.map +1 -1
- package/dist/src/utils/interactionEvent.js +62 -1
- package/dist/src/view/containerMutationHelper.d.ts.map +1 -1
- package/dist/src/view/containerMutationHelper.js +8 -0
- package/dist/src/view/dataReadiness.d.ts +2 -2
- package/dist/src/view/dataReadiness.d.ts.map +1 -1
- package/dist/src/view/dataReadiness.js +63 -58
- package/dist/src/view/facetView.js +1 -1
- package/dist/src/view/gridView/gridChild.d.ts +7 -0
- package/dist/src/view/gridView/gridChild.d.ts.map +1 -1
- package/dist/src/view/gridView/gridChild.js +180 -11
- package/dist/src/view/gridView/gridView.d.ts.map +1 -1
- package/dist/src/view/gridView/gridView.js +60 -17
- package/dist/src/view/zoom.d.ts +14 -2
- package/dist/src/view/zoom.d.ts.map +1 -1
- package/dist/src/view/zoom.js +373 -76
- package/package.json +4 -2
|
@@ -82,11 +82,29 @@ export default class InteractionController {
|
|
|
82
82
|
return this.#currentHover;
|
|
83
83
|
}
|
|
84
84
|
|
|
85
|
-
|
|
85
|
+
registerInteractionEvents() {
|
|
86
86
|
const canvas = this.#glHelper.canvas;
|
|
87
87
|
|
|
88
88
|
let lastWheelEvent = performance.now();
|
|
89
89
|
let longPressTriggered = false;
|
|
90
|
+
/** @type {{ pointerCount: 1 | 2, centerX: number, centerY: number, distance: number } | undefined} */
|
|
91
|
+
let previousTouchGesture;
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* @param {Point} point
|
|
95
|
+
* @param {import("../utils/interactionEvent.js").InteractionUiEvent} uiEvent
|
|
96
|
+
* @returns {InteractionEvent}
|
|
97
|
+
*/
|
|
98
|
+
const dispatchInteractionEvent = (point, uiEvent) => {
|
|
99
|
+
const interactionEvent = new InteractionEvent(point, uiEvent);
|
|
100
|
+
this.#viewRoot.propagateInteractionEvent(interactionEvent);
|
|
101
|
+
|
|
102
|
+
if (!this.#tooltipUpdateRequested) {
|
|
103
|
+
this.#tooltip.clear();
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
return interactionEvent;
|
|
107
|
+
};
|
|
90
108
|
|
|
91
109
|
/** @param {Event} event */
|
|
92
110
|
const listener = (event) => {
|
|
@@ -117,13 +135,7 @@ export default class InteractionController {
|
|
|
117
135
|
* @param {MouseEvent} dispatchedEvent
|
|
118
136
|
*/
|
|
119
137
|
const dispatchEvent = (dispatchedEvent) => {
|
|
120
|
-
|
|
121
|
-
new InteractionEvent(point, dispatchedEvent)
|
|
122
|
-
);
|
|
123
|
-
|
|
124
|
-
if (!this.#tooltipUpdateRequested) {
|
|
125
|
-
this.#tooltip.clear();
|
|
126
|
-
}
|
|
138
|
+
dispatchInteractionEvent(point, dispatchedEvent);
|
|
127
139
|
};
|
|
128
140
|
|
|
129
141
|
if (event.type != "wheel") {
|
|
@@ -156,26 +168,41 @@ export default class InteractionController {
|
|
|
156
168
|
|
|
157
169
|
this.#wheelInertia.cancel();
|
|
158
170
|
} else {
|
|
159
|
-
//
|
|
160
|
-
//
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
171
|
+
// We must decide on the native wheel event whether to
|
|
172
|
+
// call preventDefault() (to block page scrolling).
|
|
173
|
+
// This probe asks the pointed view hierarchy to claim
|
|
174
|
+
// wheel ownership without running real wheel side
|
|
175
|
+
// effects first. Inertia is layered on top of that
|
|
176
|
+
// decision and is not the reason for the probe.
|
|
177
|
+
const probeEvent = dispatchInteractionEvent(point, {
|
|
178
|
+
type: "wheelclaimprobe",
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
if (probeEvent.wheelClaimed) {
|
|
182
|
+
// Vertical wheeling zooms.
|
|
183
|
+
// We use inertia to generate fake wheel events for smoother zooming
|
|
184
|
+
|
|
185
|
+
const template = makeEventTemplate(wheelEvent);
|
|
186
|
+
|
|
187
|
+
this.#wheelInertia.setMomentum(
|
|
188
|
+
wheelEvent.deltaY *
|
|
189
|
+
(wheelEvent.deltaMode ? 80 : 1),
|
|
190
|
+
(delta) => {
|
|
191
|
+
const e = new WheelEvent("wheel", {
|
|
192
|
+
...template,
|
|
193
|
+
deltaMode: 0,
|
|
194
|
+
deltaX: 0,
|
|
195
|
+
deltaY: delta,
|
|
196
|
+
});
|
|
197
|
+
dispatchEvent(e);
|
|
198
|
+
}
|
|
199
|
+
);
|
|
200
|
+
|
|
201
|
+
wheelEvent.preventDefault();
|
|
202
|
+
return;
|
|
203
|
+
} else {
|
|
204
|
+
this.#wheelInertia.cancel();
|
|
205
|
+
}
|
|
179
206
|
}
|
|
180
207
|
}
|
|
181
208
|
|
|
@@ -223,11 +250,168 @@ export default class InteractionController {
|
|
|
223
250
|
"wheel",
|
|
224
251
|
"click",
|
|
225
252
|
"mousemove",
|
|
226
|
-
"gesturechange",
|
|
227
253
|
"contextmenu",
|
|
228
254
|
"dblclick",
|
|
229
255
|
].forEach((type) => canvas.addEventListener(type, listener));
|
|
230
256
|
|
|
257
|
+
/**
|
|
258
|
+
* @param {number} clientX
|
|
259
|
+
* @param {number} clientY
|
|
260
|
+
*/
|
|
261
|
+
const toCanvasPoint = (clientX, clientY) => {
|
|
262
|
+
const rect = canvas.getBoundingClientRect();
|
|
263
|
+
return new Point(
|
|
264
|
+
clientX - rect.left - canvas.clientLeft,
|
|
265
|
+
clientY - rect.top - canvas.clientTop
|
|
266
|
+
);
|
|
267
|
+
};
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* @param {TouchList} touches
|
|
271
|
+
*/
|
|
272
|
+
const readTouchGesture = (touches) => {
|
|
273
|
+
if (touches.length <= 0) {
|
|
274
|
+
return;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
const first = touches[0];
|
|
278
|
+
|
|
279
|
+
if (touches.length === 1) {
|
|
280
|
+
return {
|
|
281
|
+
pointerCount: /** @type {1} */ (1),
|
|
282
|
+
centerX: first.clientX,
|
|
283
|
+
centerY: first.clientY,
|
|
284
|
+
distance: 0,
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
const second = touches[1];
|
|
289
|
+
return {
|
|
290
|
+
pointerCount: /** @type {2} */ (2),
|
|
291
|
+
centerX: (first.clientX + second.clientX) / 2,
|
|
292
|
+
centerY: (first.clientY + second.clientY) / 2,
|
|
293
|
+
distance: getClientDistance(first, second),
|
|
294
|
+
};
|
|
295
|
+
};
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* @param {number} x
|
|
299
|
+
* @param {number} y
|
|
300
|
+
* @param {"move" | "end"} phase
|
|
301
|
+
* @param {1 | 2} pointerCount
|
|
302
|
+
* @param {number} xDelta
|
|
303
|
+
* @param {number} yDelta
|
|
304
|
+
* @param {number} zDelta
|
|
305
|
+
*/
|
|
306
|
+
const dispatchTouchGestureEvent = (
|
|
307
|
+
x,
|
|
308
|
+
y,
|
|
309
|
+
phase,
|
|
310
|
+
pointerCount,
|
|
311
|
+
xDelta,
|
|
312
|
+
yDelta,
|
|
313
|
+
zDelta
|
|
314
|
+
) => {
|
|
315
|
+
const point = toCanvasPoint(x, y);
|
|
316
|
+
dispatchInteractionEvent(point, {
|
|
317
|
+
type: "touchgesture",
|
|
318
|
+
phase,
|
|
319
|
+
pointerCount,
|
|
320
|
+
xDelta,
|
|
321
|
+
yDelta,
|
|
322
|
+
zDelta,
|
|
323
|
+
});
|
|
324
|
+
};
|
|
325
|
+
|
|
326
|
+
/**
|
|
327
|
+
* @param {TouchEvent} touchEvent
|
|
328
|
+
*/
|
|
329
|
+
const handleTouchStartOrMove = (touchEvent) => {
|
|
330
|
+
touchEvent.preventDefault();
|
|
331
|
+
this.#wheelInertia.cancel();
|
|
332
|
+
this.#tooltipUpdateRequested = false;
|
|
333
|
+
|
|
334
|
+
const currentGesture = readTouchGesture(touchEvent.touches);
|
|
335
|
+
if (!currentGesture) {
|
|
336
|
+
previousTouchGesture = undefined;
|
|
337
|
+
return;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
if (
|
|
341
|
+
!previousTouchGesture ||
|
|
342
|
+
previousTouchGesture.pointerCount !==
|
|
343
|
+
currentGesture.pointerCount
|
|
344
|
+
) {
|
|
345
|
+
previousTouchGesture = currentGesture;
|
|
346
|
+
return;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
const xDelta =
|
|
350
|
+
currentGesture.centerX - previousTouchGesture.centerX;
|
|
351
|
+
const yDelta =
|
|
352
|
+
currentGesture.centerY - previousTouchGesture.centerY;
|
|
353
|
+
const zDelta =
|
|
354
|
+
currentGesture.pointerCount === 2
|
|
355
|
+
? pinchDistanceToZoomDelta(
|
|
356
|
+
previousTouchGesture.distance,
|
|
357
|
+
currentGesture.distance
|
|
358
|
+
)
|
|
359
|
+
: 0;
|
|
360
|
+
|
|
361
|
+
if (
|
|
362
|
+
(xDelta !== 0 || yDelta !== 0 || zDelta !== 0) &&
|
|
363
|
+
Number.isFinite(xDelta) &&
|
|
364
|
+
Number.isFinite(yDelta) &&
|
|
365
|
+
Number.isFinite(zDelta)
|
|
366
|
+
) {
|
|
367
|
+
dispatchTouchGestureEvent(
|
|
368
|
+
previousTouchGesture.centerX,
|
|
369
|
+
previousTouchGesture.centerY,
|
|
370
|
+
"move",
|
|
371
|
+
currentGesture.pointerCount,
|
|
372
|
+
xDelta,
|
|
373
|
+
yDelta,
|
|
374
|
+
zDelta
|
|
375
|
+
);
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
previousTouchGesture = currentGesture;
|
|
379
|
+
};
|
|
380
|
+
|
|
381
|
+
/**
|
|
382
|
+
* @param {TouchEvent} touchEvent
|
|
383
|
+
*/
|
|
384
|
+
const handleTouchEndOrCancel = (touchEvent) => {
|
|
385
|
+
touchEvent.preventDefault();
|
|
386
|
+
this.#tooltipUpdateRequested = false;
|
|
387
|
+
if (previousTouchGesture && touchEvent.touches.length === 0) {
|
|
388
|
+
dispatchTouchGestureEvent(
|
|
389
|
+
previousTouchGesture.centerX,
|
|
390
|
+
previousTouchGesture.centerY,
|
|
391
|
+
"end",
|
|
392
|
+
previousTouchGesture.pointerCount,
|
|
393
|
+
0,
|
|
394
|
+
0,
|
|
395
|
+
0
|
|
396
|
+
);
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
previousTouchGesture = readTouchGesture(touchEvent.touches);
|
|
400
|
+
};
|
|
401
|
+
|
|
402
|
+
canvas.addEventListener("touchstart", handleTouchStartOrMove, {
|
|
403
|
+
passive: false,
|
|
404
|
+
});
|
|
405
|
+
canvas.addEventListener("touchmove", handleTouchStartOrMove, {
|
|
406
|
+
passive: false,
|
|
407
|
+
});
|
|
408
|
+
canvas.addEventListener("touchend", handleTouchEndOrCancel, {
|
|
409
|
+
passive: false,
|
|
410
|
+
});
|
|
411
|
+
canvas.addEventListener("touchcancel", handleTouchEndOrCancel, {
|
|
412
|
+
passive: false,
|
|
413
|
+
});
|
|
414
|
+
|
|
231
415
|
canvas.addEventListener("mousedown", (/** @type {MouseEvent} */ e) => {
|
|
232
416
|
this.#mouseDownCoords = Point.fromMouseEvent(e);
|
|
233
417
|
if (this.#tooltip.sticky) {
|
|
@@ -375,3 +559,34 @@ export default class InteractionController {
|
|
|
375
559
|
}
|
|
376
560
|
}
|
|
377
561
|
}
|
|
562
|
+
|
|
563
|
+
/**
|
|
564
|
+
* @typedef {{clientX: number, clientY: number}} ClientPointLike
|
|
565
|
+
*/
|
|
566
|
+
|
|
567
|
+
/**
|
|
568
|
+
* Returns euclidean distance between two client-space points.
|
|
569
|
+
*
|
|
570
|
+
* @param {ClientPointLike} a
|
|
571
|
+
* @param {ClientPointLike} b
|
|
572
|
+
*/
|
|
573
|
+
function getClientDistance(a, b) {
|
|
574
|
+
const dx = b.clientX - a.clientX;
|
|
575
|
+
const dy = b.clientY - a.clientY;
|
|
576
|
+
return Math.hypot(dx, dy);
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
/**
|
|
580
|
+
* Converts a pinch distance ratio to a zDelta used by interactionToZoom:
|
|
581
|
+
* scaleFactor = 2 ** zDelta.
|
|
582
|
+
*
|
|
583
|
+
* @param {number} previousDistance
|
|
584
|
+
* @param {number} currentDistance
|
|
585
|
+
*/
|
|
586
|
+
function pinchDistanceToZoomDelta(previousDistance, currentDistance) {
|
|
587
|
+
if (previousDistance <= 0 || currentDistance <= 0) {
|
|
588
|
+
return 0;
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
return Math.log2(previousDistance / currentDistance);
|
|
592
|
+
}
|
|
@@ -70,7 +70,7 @@ export default class RenderCoordinator {
|
|
|
70
70
|
const commonOptions = {
|
|
71
71
|
webGLHelper: this.#glHelper,
|
|
72
72
|
canvasSize,
|
|
73
|
-
devicePixelRatio:
|
|
73
|
+
devicePixelRatio: this.#glHelper.getDevicePixelRatio(canvasSize),
|
|
74
74
|
};
|
|
75
75
|
|
|
76
76
|
this.#renderingContext = new BufferedViewRenderingContext(
|
package/dist/src/genomeSpy.d.ts
CHANGED
|
@@ -77,7 +77,13 @@ export default class GenomeSpy {
|
|
|
77
77
|
*/
|
|
78
78
|
launch(): Promise<boolean>;
|
|
79
79
|
initializeVisibleViewData(): Promise<void>;
|
|
80
|
-
|
|
80
|
+
/**
|
|
81
|
+
* Waits until lazy sources under the root view have loaded data for the
|
|
82
|
+
* current visible positional domain.
|
|
83
|
+
*
|
|
84
|
+
* @param {AbortSignal} [signal]
|
|
85
|
+
*/
|
|
86
|
+
awaitVisibleLazyData(signal?: AbortSignal): Promise<void>;
|
|
81
87
|
/**
|
|
82
88
|
* This method should be called in a mouseMove handler. If not called, the
|
|
83
89
|
* tooltip will be hidden.
|
|
@@ -98,8 +104,12 @@ export default class GenomeSpy {
|
|
|
98
104
|
*/
|
|
99
105
|
exportCanvas(logicalWidth?: number, logicalHeight?: number, devicePixelRatio?: number, clearColor?: string): string;
|
|
100
106
|
getLogicalCanvasSize(): {
|
|
101
|
-
width:
|
|
102
|
-
height:
|
|
107
|
+
width: number;
|
|
108
|
+
height: number;
|
|
109
|
+
};
|
|
110
|
+
getRenderedBounds(): {
|
|
111
|
+
width: number | undefined;
|
|
112
|
+
height: number | undefined;
|
|
103
113
|
};
|
|
104
114
|
computeLayout(): void;
|
|
105
115
|
renderAll(): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"genomeSpy.d.ts","sourceRoot":"","sources":["../../src/genomeSpy.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"genomeSpy.d.ts","sourceRoot":"","sources":["../../src/genomeSpy.js"],"names":[],"mappings":"AAyDA;IAoBI;;;;;OAKG;IAEH;;;;;OAKG;IACH,uBAJW,WAAW,qDAEX,OAAO,qBAAqB,EAAE,YAAY,EA0CpD;IAvCG,uBAA0B;IAC1B,oDAAsB;IAItB,sCAAsC;IACtC,wCAAgB;IAEhB,yBAAoC;IAEpC,4CAA4C;IAC5C,oBADW,CAAC,CAAS,IAAM,EAAN,MAAM,KAAE,MAAM,EAAE,CAAC,EAAE,CACZ;IAE5B,mBAAoD;IAEpD,0BAA0B;IAC1B,aADW,WAAW,CACM;IAE5B;;;;;OAKG;IACH,yBAFU,CAAC,IAAI,qEAAM,KAAK,OAAO,CAE8B;IAE/D,oFAAoF;IACpF,iBADW,MAAM,CAAC,MAAM,EAAE,OAAO,6BAA6B,EAAE,cAAc,CAAC,CAK9E;IAED,mBAAmB;IACnB,8EAAyB;IAIzB,YAAkC;IAatC;;;OAGG;IACH,oCAFW,CAAC,IAAI,EAAE,MAAM,KAAK,GAAG,EAAE,QAIjC;IAED;;OAEG;IACH,+BAFW,MAAM,YAShB;IAED;;;;OAIG;IACH,sBAHW,MAAM,QACN,GAAG,EAAE,QAYf;IAED;;;OAGG;IACH,uBAHW,MAAM,YACN,CAAC,KAAK,EAAE,GAAG,KAAK,IAAI,QAI9B;IAED;;;OAGG;IACH,0BAHW,MAAM,YACN,CAAC,KAAK,EAAE,GAAG,KAAK,IAAI,QAI9B;IAED;;;;;OAKG;IACH,gBAHW,kBAAkB,YAClB,GAAG,QAMb;IAgEG,iDAAsB;IAQ1B;;OAEG;IACH,gBAqBC;IA0LD;;;OAGG;IACH,UAFa,OAAO,CAAC,OAAO,CAAC,CAyC5B;IAED,2CAiBC;IAED;;;;;OAKG;IACH,8BAFW,WAAW,iBAgBrB;IAED;;;;;;;OAOG;IACH,cAFa,CAAC,SAFH,CAAC,cACD,CAAS,IAAC,EAAD,CAAC,KAAE,OAAO,CAAC,MAAM,GAAG,WAAW,GAAG,OAAO,KAAK,EAAE,cAAc,CAAC,QAKlF;IAED;;;;;;;;OAQG;IACH,4BANW,MAAM,kBACN,MAAM,qBACN,MAAM,eACN,MAAM,UAuBhB;IAED;;;MAEC;IAED;eACwB,MAAM,GAAG,SAAS;gBAAU,MAAM,GAAG,SAAS;MAcrE;IAED,sBAEC;IAED,kBAEC;IAED,iCAEC;IAED,oEAYC;IAED,uFAWC;;CACJ;;;;iCAlnBY,eAAe,GAAG,QAAQ,GAAG,gBAAgB,GAAG,kBAAkB;4BApBnC,uBAAuB;qBAR9C,qBAAqB;wBAElB,yBAAyB;qBAL5B,oBAAoB"}
|
package/dist/src/genomeSpy.js
CHANGED
|
@@ -38,6 +38,12 @@ import {
|
|
|
38
38
|
import { exportCanvas } from "./genomeSpy/canvasExport.js";
|
|
39
39
|
import { validateSelectorConstraints } from "./view/viewSelectors.js";
|
|
40
40
|
import parquet from "./data/formats/parquet.js";
|
|
41
|
+
import bed from "./data/formats/bed.js";
|
|
42
|
+
import bedpe from "./data/formats/bedpe.js";
|
|
43
|
+
import SingleAxisWindowedSource from "./data/sources/lazy/singleAxisWindowedSource.js";
|
|
44
|
+
import { ensureAssembliesForView } from "./genome/assemblyPreflight.js";
|
|
45
|
+
import { resolveRootGenomeConfig } from "./genome/rootGenomeConfig.js";
|
|
46
|
+
import { awaitSubtreeLazyReady } from "./view/dataReadiness.js";
|
|
41
47
|
|
|
42
48
|
/**
|
|
43
49
|
* Events that are broadcasted to all views.
|
|
@@ -46,6 +52,8 @@ import parquet from "./data/formats/parquet.js";
|
|
|
46
52
|
|
|
47
53
|
vegaFormats("fasta", fasta);
|
|
48
54
|
vegaFormats("parquet", parquet);
|
|
55
|
+
vegaFormats("bed", bed);
|
|
56
|
+
vegaFormats("bedpe", bedpe);
|
|
49
57
|
|
|
50
58
|
export default class GenomeSpy {
|
|
51
59
|
/** @type {(() => void)[]} */
|
|
@@ -198,6 +206,8 @@ export default class GenomeSpy {
|
|
|
198
206
|
}
|
|
199
207
|
|
|
200
208
|
#setupDpr() {
|
|
209
|
+
this.dpr = this.#glHelper.getDevicePixelRatio();
|
|
210
|
+
|
|
201
211
|
const dprSetter = this.viewRoot.paramRuntime.allocateSetter(
|
|
202
212
|
"devicePixelRatio",
|
|
203
213
|
this.dpr
|
|
@@ -205,7 +215,7 @@ export default class GenomeSpy {
|
|
|
205
215
|
|
|
206
216
|
const resizeCallback = () => {
|
|
207
217
|
this.#glHelper.invalidateSize();
|
|
208
|
-
this.dpr =
|
|
218
|
+
this.dpr = this.#glHelper.getDevicePixelRatio();
|
|
209
219
|
dprSetter(this.dpr);
|
|
210
220
|
this.computeLayout();
|
|
211
221
|
// Render immediately, without RAF
|
|
@@ -305,9 +315,15 @@ export default class GenomeSpy {
|
|
|
305
315
|
}
|
|
306
316
|
|
|
307
317
|
async #initializeGenomeStore() {
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
318
|
+
this.genomeStore = new GenomeStore(this.spec.baseUrl);
|
|
319
|
+
|
|
320
|
+
const { genomesByName, defaultAssembly, deprecationWarning } =
|
|
321
|
+
resolveRootGenomeConfig(this.spec);
|
|
322
|
+
this.genomeStore.configureGenomes(genomesByName, defaultAssembly);
|
|
323
|
+
|
|
324
|
+
if (deprecationWarning) {
|
|
325
|
+
// eslint-disable-next-line no-console
|
|
326
|
+
console.warn(deprecationWarning);
|
|
311
327
|
}
|
|
312
328
|
}
|
|
313
329
|
|
|
@@ -398,6 +414,11 @@ export default class GenomeSpy {
|
|
|
398
414
|
VIEW_ROOT_NAME
|
|
399
415
|
);
|
|
400
416
|
|
|
417
|
+
// Reminder: assemblies must be ensured after view creation (imports and
|
|
418
|
+
// inheritance resolved), but before any code path that may touch scales
|
|
419
|
+
// (e.g. step-based sizes, dynamic opacity, encoder initialization).
|
|
420
|
+
await ensureAssembliesForView(this.viewRoot, this.genomeStore);
|
|
421
|
+
|
|
401
422
|
this.#loadingStatusRegistry.set(this.viewRoot, "loading");
|
|
402
423
|
|
|
403
424
|
this.#canvasWrapper.style.flexGrow =
|
|
@@ -475,7 +496,7 @@ export default class GenomeSpy {
|
|
|
475
496
|
|
|
476
497
|
await this.#prepareViewsAndData();
|
|
477
498
|
|
|
478
|
-
this.
|
|
499
|
+
this.#interactionController.registerInteractionEvents();
|
|
479
500
|
|
|
480
501
|
this.computeLayout();
|
|
481
502
|
this.animator.requestRender();
|
|
@@ -528,8 +549,26 @@ export default class GenomeSpy {
|
|
|
528
549
|
this.animator.requestRender();
|
|
529
550
|
}
|
|
530
551
|
|
|
531
|
-
|
|
532
|
-
|
|
552
|
+
/**
|
|
553
|
+
* Waits until lazy sources under the root view have loaded data for the
|
|
554
|
+
* current visible positional domain.
|
|
555
|
+
*
|
|
556
|
+
* @param {AbortSignal} [signal]
|
|
557
|
+
*/
|
|
558
|
+
async awaitVisibleLazyData(signal) {
|
|
559
|
+
if (!this.viewRoot) {
|
|
560
|
+
return;
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
await awaitSubtreeLazyReady(
|
|
564
|
+
this.viewRoot.context,
|
|
565
|
+
this.viewRoot,
|
|
566
|
+
undefined,
|
|
567
|
+
signal,
|
|
568
|
+
(view) =>
|
|
569
|
+
view.isConfiguredVisible() &&
|
|
570
|
+
hasWindowedLazyDataSource(view)
|
|
571
|
+
);
|
|
533
572
|
}
|
|
534
573
|
|
|
535
574
|
/**
|
|
@@ -579,6 +618,23 @@ export default class GenomeSpy {
|
|
|
579
618
|
return this.#glHelper.getLogicalCanvasSize();
|
|
580
619
|
}
|
|
581
620
|
|
|
621
|
+
getRenderedBounds() {
|
|
622
|
+
/** @type {{ width: number | undefined, height: number | undefined }} */
|
|
623
|
+
const bounds = {
|
|
624
|
+
width: undefined,
|
|
625
|
+
height: undefined,
|
|
626
|
+
};
|
|
627
|
+
|
|
628
|
+
this.viewRoot.visit((view) => {
|
|
629
|
+
for (const coords of view.facetCoords.values()) {
|
|
630
|
+
bounds.width = Math.max(bounds.width ?? 0, coords.x2);
|
|
631
|
+
bounds.height = Math.max(bounds.height ?? 0, coords.y2);
|
|
632
|
+
}
|
|
633
|
+
});
|
|
634
|
+
|
|
635
|
+
return bounds;
|
|
636
|
+
}
|
|
637
|
+
|
|
582
638
|
computeLayout() {
|
|
583
639
|
this.#renderCoordinator.computeLayout();
|
|
584
640
|
}
|
|
@@ -618,3 +674,21 @@ export default class GenomeSpy {
|
|
|
618
674
|
return resolutions;
|
|
619
675
|
}
|
|
620
676
|
}
|
|
677
|
+
|
|
678
|
+
/**
|
|
679
|
+
* @param {View} view
|
|
680
|
+
*/
|
|
681
|
+
function hasWindowedLazyDataSource(view) {
|
|
682
|
+
/** @type {View | null} */
|
|
683
|
+
let current = view;
|
|
684
|
+
|
|
685
|
+
while (current) {
|
|
686
|
+
const dataSource = current.flowHandle?.dataSource;
|
|
687
|
+
if (dataSource) {
|
|
688
|
+
return dataSource instanceof SingleAxisWindowedSource;
|
|
689
|
+
}
|
|
690
|
+
current = current.dataParent;
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
return false;
|
|
694
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Handles logical/physical canvas size calculations and optional
|
|
3
|
+
* device-pixel-content-box observation.
|
|
4
|
+
*/
|
|
5
|
+
export default class CanvasSizeHelper {
|
|
6
|
+
/**
|
|
7
|
+
* @param {HTMLElement} container
|
|
8
|
+
* @param {HTMLCanvasElement} canvas
|
|
9
|
+
* @param {() => {width: number, height: number}} sizeSource
|
|
10
|
+
* @param {() => void} [onPhysicalSizeChange]
|
|
11
|
+
*/
|
|
12
|
+
constructor(container: HTMLElement, canvas: HTMLCanvasElement, sizeSource: () => {
|
|
13
|
+
width: number;
|
|
14
|
+
height: number;
|
|
15
|
+
}, onPhysicalSizeChange?: () => void);
|
|
16
|
+
_container: HTMLElement;
|
|
17
|
+
_canvas: HTMLCanvasElement;
|
|
18
|
+
_sizeSource: () => {
|
|
19
|
+
width: number;
|
|
20
|
+
height: number;
|
|
21
|
+
};
|
|
22
|
+
_onPhysicalSizeChange: () => void;
|
|
23
|
+
/**
|
|
24
|
+
* @type {{ width: number, height: number } | undefined}
|
|
25
|
+
*/
|
|
26
|
+
_logicalCanvasSize: {
|
|
27
|
+
width: number;
|
|
28
|
+
height: number;
|
|
29
|
+
} | undefined;
|
|
30
|
+
/**
|
|
31
|
+
* @type {{ width: number, height: number } | undefined}
|
|
32
|
+
*/
|
|
33
|
+
_devicePixelContentBoxSize: {
|
|
34
|
+
width: number;
|
|
35
|
+
height: number;
|
|
36
|
+
} | undefined;
|
|
37
|
+
/**
|
|
38
|
+
* @type {ResizeObserver | undefined}
|
|
39
|
+
*/
|
|
40
|
+
_devicePixelContentBoxObserver: ResizeObserver | undefined;
|
|
41
|
+
invalidate(): void;
|
|
42
|
+
finalize(): void;
|
|
43
|
+
/**
|
|
44
|
+
* Returns the canvas size in true display pixels
|
|
45
|
+
*
|
|
46
|
+
* @param {{ width: number, height: number }} [logicalSize]
|
|
47
|
+
*/
|
|
48
|
+
getPhysicalCanvasSize(logicalSize?: {
|
|
49
|
+
width: number;
|
|
50
|
+
height: number;
|
|
51
|
+
}): {
|
|
52
|
+
width: number;
|
|
53
|
+
height: number;
|
|
54
|
+
};
|
|
55
|
+
/**
|
|
56
|
+
* Returns the ratio between true display pixels and logical pixels.
|
|
57
|
+
*
|
|
58
|
+
* @param {{ width: number, height: number }} [logicalSize]
|
|
59
|
+
*/
|
|
60
|
+
getDevicePixelRatio(logicalSize?: {
|
|
61
|
+
width: number;
|
|
62
|
+
height: number;
|
|
63
|
+
}): number;
|
|
64
|
+
/**
|
|
65
|
+
* Returns the size of the canvas canvas container size in logical pixels,
|
|
66
|
+
* without devicePixelRatio correction.
|
|
67
|
+
*/
|
|
68
|
+
getLogicalCanvasSize(): {
|
|
69
|
+
width: number;
|
|
70
|
+
height: number;
|
|
71
|
+
};
|
|
72
|
+
_observeDevicePixelContentBox(): void;
|
|
73
|
+
}
|
|
74
|
+
//# sourceMappingURL=canvasSizeHelper.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"canvasSizeHelper.d.ts","sourceRoot":"","sources":["../../../src/gl/canvasSizeHelper.js"],"names":[],"mappings":"AAAA;;;GAGG;AACH;IACI;;;;;OAKG;IACH,uBALW,WAAW,UACX,iBAAiB,cACjB,MAAM;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAC,yBACrC,MAAM,IAAI,EAwBpB;IArBG,wBAA2B;IAC3B,2BAAqB;IACrB,mBANa;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAC,CAMf;IAC7B,6BANa,IAAI,CAM8C;IAE/D;;OAEG;IACH,oBAFU;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,GAAG,SAAS,CAEpB;IAEnC;;OAEG;IACH,4BAFU;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,GAAG,SAAS,CAEZ;IAE3C;;OAEG;IACH,gCAFU,cAAc,GAAG,SAAS,CAEW;IAKnD,mBAGC;IAED,iBAIC;IAED;;;;OAIG;IACH,oCAFW;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE;eA1BrB,MAAM;gBAAU,MAAM;MA0C5C;IAED;;;;OAIG;IACH,kCAFW;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,UA2B3C;IAED;;;OAGG;IACH;eArFuB,MAAM;gBAAU,MAAM;MA8H5C;IAED,sCAuDC;CACJ"}
|