@jbrowse/plugin-dotplot-view 3.6.4 → 3.7.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/dist/DiagonalizeDotplotRpc.d.ts +30 -0
- package/dist/DiagonalizeDotplotRpc.js +156 -0
- package/dist/DotplotDisplay/renderDotplotBlock.js +3 -0
- package/dist/DotplotDisplay/stateModelFactory.d.ts +6 -0
- package/dist/DotplotDisplay/stateModelFactory.js +15 -0
- package/dist/DotplotRenderer/DotplotRenderer.d.ts +3 -12
- package/dist/DotplotRenderer/clamp.d.ts +7 -0
- package/dist/DotplotRenderer/clamp.js +62 -0
- package/dist/DotplotRenderer/drawDotplot.d.ts +5 -4
- package/dist/DotplotRenderer/drawDotplot.js +92 -96
- package/dist/DotplotView/1dview.js +5 -3
- package/dist/DotplotView/components/ColorBySelector.d.ts +5 -0
- package/dist/DotplotView/components/ColorBySelector.js +79 -0
- package/dist/DotplotView/components/DiagonalizationProgressDialog.d.ts +6 -0
- package/dist/DotplotView/components/DiagonalizationProgressDialog.js +125 -0
- package/dist/DotplotView/components/DotplotControls.js +84 -12
- package/dist/DotplotView/components/DotplotTooltips.d.ts +15 -0
- package/dist/DotplotView/components/DotplotTooltips.js +43 -0
- package/dist/DotplotView/components/DotplotView.js +16 -191
- package/dist/DotplotView/components/DotplotWarnings.js +3 -3
- package/dist/DotplotView/components/ImportForm/index.js +0 -1
- package/dist/DotplotView/components/MinLengthSlider.d.ts +5 -0
- package/dist/DotplotView/components/MinLengthSlider.js +44 -0
- package/dist/DotplotView/components/MouseInteractionLayer.d.ts +17 -0
- package/dist/DotplotView/components/MouseInteractionLayer.js +18 -0
- package/dist/DotplotView/components/OpacitySlider.d.ts +5 -0
- package/dist/DotplotView/components/OpacitySlider.js +43 -0
- package/dist/DotplotView/components/SelectionContextMenu.d.ts +13 -0
- package/dist/DotplotView/components/SelectionContextMenu.js +42 -0
- package/dist/DotplotView/components/SliderTooltip.d.ts +2 -0
- package/dist/DotplotView/components/SliderTooltip.js +9 -0
- package/dist/DotplotView/components/hooks/useCtrlKeyTracking.d.ts +1 -0
- package/dist/DotplotView/components/hooks/useCtrlKeyTracking.js +24 -0
- package/dist/DotplotView/components/hooks/useCursorMode.d.ts +7 -0
- package/dist/DotplotView/components/hooks/useCursorMode.js +19 -0
- package/dist/DotplotView/components/hooks/useMouseCoordinates.d.ts +29 -0
- package/dist/DotplotView/components/hooks/useMouseCoordinates.js +52 -0
- package/dist/DotplotView/components/hooks/useMouseMoveHandler.d.ts +6 -0
- package/dist/DotplotView/components/hooks/useMouseMoveHandler.js +27 -0
- package/dist/DotplotView/components/hooks/useMouseUpHandler.d.ts +3 -0
- package/dist/DotplotView/components/hooks/useMouseUpHandler.js +31 -0
- package/dist/DotplotView/components/hooks/useWheelHandler.d.ts +8 -0
- package/dist/DotplotView/components/hooks/useWheelHandler.js +47 -0
- package/dist/DotplotView/components/util.js +1 -3
- package/dist/DotplotView/model.d.ts +5 -0
- package/dist/DotplotView/model.js +35 -20
- package/dist/ServerSideRenderedBlockContent.js +3 -20
- package/dist/index.js +2 -0
- package/esm/DiagonalizeDotplotRpc.d.ts +30 -0
- package/esm/DiagonalizeDotplotRpc.js +150 -0
- package/esm/DotplotDisplay/renderDotplotBlock.js +3 -0
- package/esm/DotplotDisplay/stateModelFactory.d.ts +6 -0
- package/esm/DotplotDisplay/stateModelFactory.js +15 -0
- package/esm/DotplotRenderer/DotplotRenderer.d.ts +3 -12
- package/esm/DotplotRenderer/clamp.d.ts +7 -0
- package/esm/DotplotRenderer/clamp.js +58 -0
- package/esm/DotplotRenderer/drawDotplot.d.ts +5 -4
- package/esm/DotplotRenderer/drawDotplot.js +92 -96
- package/esm/DotplotView/1dview.js +5 -3
- package/esm/DotplotView/components/ColorBySelector.d.ts +5 -0
- package/esm/DotplotView/components/ColorBySelector.js +74 -0
- package/esm/DotplotView/components/DiagonalizationProgressDialog.d.ts +6 -0
- package/esm/DotplotView/components/DiagonalizationProgressDialog.js +123 -0
- package/esm/DotplotView/components/DotplotControls.js +52 -13
- package/esm/DotplotView/components/DotplotTooltips.d.ts +15 -0
- package/esm/DotplotView/components/DotplotTooltips.js +7 -0
- package/esm/DotplotView/components/DotplotView.js +17 -159
- package/esm/DotplotView/components/DotplotWarnings.js +4 -4
- package/esm/DotplotView/components/ImportForm/index.js +0 -1
- package/esm/DotplotView/components/MinLengthSlider.d.ts +5 -0
- package/esm/DotplotView/components/MinLengthSlider.js +39 -0
- package/esm/DotplotView/components/MouseInteractionLayer.d.ts +17 -0
- package/esm/DotplotView/components/MouseInteractionLayer.js +12 -0
- package/esm/DotplotView/components/OpacitySlider.d.ts +5 -0
- package/esm/DotplotView/components/OpacitySlider.js +38 -0
- package/esm/DotplotView/components/SelectionContextMenu.d.ts +13 -0
- package/esm/DotplotView/components/SelectionContextMenu.js +39 -0
- package/esm/DotplotView/components/SliderTooltip.d.ts +2 -0
- package/esm/DotplotView/components/SliderTooltip.js +6 -0
- package/esm/DotplotView/components/hooks/useCtrlKeyTracking.d.ts +1 -0
- package/esm/DotplotView/components/hooks/useCtrlKeyTracking.js +21 -0
- package/esm/DotplotView/components/hooks/useCursorMode.d.ts +7 -0
- package/esm/DotplotView/components/hooks/useCursorMode.js +16 -0
- package/esm/DotplotView/components/hooks/useMouseCoordinates.d.ts +29 -0
- package/esm/DotplotView/components/hooks/useMouseCoordinates.js +49 -0
- package/esm/DotplotView/components/hooks/useMouseMoveHandler.d.ts +6 -0
- package/esm/DotplotView/components/hooks/useMouseMoveHandler.js +24 -0
- package/esm/DotplotView/components/hooks/useMouseUpHandler.d.ts +3 -0
- package/esm/DotplotView/components/hooks/useMouseUpHandler.js +28 -0
- package/esm/DotplotView/components/hooks/useWheelHandler.d.ts +8 -0
- package/esm/DotplotView/components/hooks/useWheelHandler.js +44 -0
- package/esm/DotplotView/components/util.js +1 -3
- package/esm/DotplotView/model.d.ts +5 -0
- package/esm/DotplotView/model.js +35 -20
- package/esm/ServerSideRenderedBlockContent.js +4 -21
- package/esm/index.js +2 -0
- package/package.json +4 -4
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.useMouseMoveHandler = useMouseMoveHandler;
|
|
4
|
+
const react_1 = require("react");
|
|
5
|
+
function useMouseMoveHandler(mousecurrClient, mousedownClient, mouseupClient, validPan, hview, vview, setMouseCurrClient) {
|
|
6
|
+
(0, react_1.useEffect)(() => {
|
|
7
|
+
function globalMouseMove(event) {
|
|
8
|
+
setMouseCurrClient([event.clientX, event.clientY]);
|
|
9
|
+
if (mousecurrClient && mousedownClient && validPan && !mouseupClient) {
|
|
10
|
+
hview.scroll(-event.clientX + mousecurrClient[0]);
|
|
11
|
+
vview.scroll(event.clientY - mousecurrClient[1]);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
window.addEventListener('mousemove', globalMouseMove);
|
|
15
|
+
return () => {
|
|
16
|
+
window.removeEventListener('mousemove', globalMouseMove);
|
|
17
|
+
};
|
|
18
|
+
}, [
|
|
19
|
+
validPan,
|
|
20
|
+
mousecurrClient,
|
|
21
|
+
mousedownClient,
|
|
22
|
+
mouseupClient,
|
|
23
|
+
hview,
|
|
24
|
+
vview,
|
|
25
|
+
setMouseCurrClient,
|
|
26
|
+
]);
|
|
27
|
+
}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
type Coord = [number, number] | undefined;
|
|
2
|
+
export declare function useMouseUpHandler(mousedown: Coord, mouseup: Coord, xdistance: number, ydistance: number, validSelect: boolean, setMouseUpClient: (coord: Coord) => void, setMouseDownClient: (coord: Coord) => void): void;
|
|
3
|
+
export {};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.useMouseUpHandler = useMouseUpHandler;
|
|
4
|
+
const react_1 = require("react");
|
|
5
|
+
function useMouseUpHandler(mousedown, mouseup, xdistance, ydistance, validSelect, setMouseUpClient, setMouseDownClient) {
|
|
6
|
+
(0, react_1.useEffect)(() => {
|
|
7
|
+
function globalMouseUp(event) {
|
|
8
|
+
if (Math.abs(xdistance) > 3 && Math.abs(ydistance) > 3 && validSelect) {
|
|
9
|
+
setMouseUpClient([event.clientX, event.clientY]);
|
|
10
|
+
}
|
|
11
|
+
else {
|
|
12
|
+
setMouseDownClient(undefined);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
if (mousedown && !mouseup) {
|
|
16
|
+
window.addEventListener('mouseup', globalMouseUp, true);
|
|
17
|
+
return () => {
|
|
18
|
+
window.removeEventListener('mouseup', globalMouseUp, true);
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
return () => { };
|
|
22
|
+
}, [
|
|
23
|
+
validSelect,
|
|
24
|
+
mousedown,
|
|
25
|
+
mouseup,
|
|
26
|
+
xdistance,
|
|
27
|
+
ydistance,
|
|
28
|
+
setMouseUpClient,
|
|
29
|
+
setMouseDownClient,
|
|
30
|
+
]);
|
|
31
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
type Coord = [number, number] | undefined;
|
|
2
|
+
interface View {
|
|
3
|
+
scroll: (distance: number) => void;
|
|
4
|
+
zoomTo: (bpPerPx: number, position: number) => void;
|
|
5
|
+
bpPerPx: number;
|
|
6
|
+
}
|
|
7
|
+
export declare function useWheelHandler(ref: React.RefObject<HTMLDivElement | null>, wheelMode: string, hview: View, vview: View, mousecurr: Coord, rootRectHeight: number): void;
|
|
8
|
+
export {};
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.useWheelHandler = useWheelHandler;
|
|
4
|
+
const react_1 = require("react");
|
|
5
|
+
const mobx_1 = require("mobx");
|
|
6
|
+
function useWheelHandler(ref, wheelMode, hview, vview, mousecurr, rootRectHeight) {
|
|
7
|
+
const distanceX = (0, react_1.useRef)(0);
|
|
8
|
+
const distanceY = (0, react_1.useRef)(0);
|
|
9
|
+
const scheduled = (0, react_1.useRef)(false);
|
|
10
|
+
(0, react_1.useEffect)(() => {
|
|
11
|
+
function onWheel(event) {
|
|
12
|
+
event.preventDefault();
|
|
13
|
+
distanceX.current += event.deltaX;
|
|
14
|
+
distanceY.current -= event.deltaY;
|
|
15
|
+
if (!scheduled.current) {
|
|
16
|
+
scheduled.current = true;
|
|
17
|
+
window.requestAnimationFrame(() => {
|
|
18
|
+
(0, mobx_1.transaction)(() => {
|
|
19
|
+
if (wheelMode === 'pan') {
|
|
20
|
+
hview.scroll(distanceX.current / 3);
|
|
21
|
+
vview.scroll(distanceY.current / 10);
|
|
22
|
+
}
|
|
23
|
+
else if (wheelMode === 'zoom') {
|
|
24
|
+
if (Math.abs(distanceY.current) > Math.abs(distanceX.current) * 2 &&
|
|
25
|
+
mousecurr) {
|
|
26
|
+
const val = distanceY.current < 0 ? 1.1 : 0.9;
|
|
27
|
+
hview.zoomTo(hview.bpPerPx * val, mousecurr[0]);
|
|
28
|
+
vview.zoomTo(vview.bpPerPx * val, rootRectHeight - mousecurr[1]);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
scheduled.current = false;
|
|
33
|
+
distanceX.current = 0;
|
|
34
|
+
distanceY.current = 0;
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
if (ref.current) {
|
|
39
|
+
const curr = ref.current;
|
|
40
|
+
curr.addEventListener('wheel', onWheel);
|
|
41
|
+
return () => {
|
|
42
|
+
curr.removeEventListener('wheel', onWheel);
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
return () => { };
|
|
46
|
+
}, [hview, vview, wheelMode, mousecurr, rootRectHeight, ref]);
|
|
47
|
+
}
|
|
@@ -34,9 +34,7 @@ function getBlockLabelKeysToHide(blocks, length, viewOffsetPx) {
|
|
|
34
34
|
function chooseGridPitch(scale, minMajorPitchPx, minMinorPitchPx) {
|
|
35
35
|
scale = Math.abs(scale);
|
|
36
36
|
const minMajorPitchBp = minMajorPitchPx * scale;
|
|
37
|
-
const majorMagnitude = +
|
|
38
|
-
.toExponential()
|
|
39
|
-
.split(/e/i)[1];
|
|
37
|
+
const majorMagnitude = +minMajorPitchBp.toExponential().split(/e/i)[1];
|
|
40
38
|
let majorPitch = 10 ** majorMagnitude;
|
|
41
39
|
while (majorPitch < minMajorPitchBp) {
|
|
42
40
|
majorPitch *= 2;
|
|
@@ -390,6 +390,7 @@ export default function stateModelFactory(pm: PluginManager): import("mobx-state
|
|
|
390
390
|
setError(e: unknown): void;
|
|
391
391
|
zoomOut(): void;
|
|
392
392
|
zoomIn(): void;
|
|
393
|
+
} & {
|
|
393
394
|
activateTrackSelector(): import("@jbrowse/core/util").Widget;
|
|
394
395
|
showTrack(trackId: string, initialSnapshot?: {}): void;
|
|
395
396
|
hideTrack(trackId: string): number;
|
|
@@ -408,6 +409,10 @@ export default function stateModelFactory(pm: PluginManager): import("mobx-state
|
|
|
408
409
|
reversed?: boolean;
|
|
409
410
|
}[] | undefined;
|
|
410
411
|
zoomInToMouseCoords(mousedown: Coord, mouseup: Coord): void;
|
|
412
|
+
calculateBorders(): {
|
|
413
|
+
borderX: number;
|
|
414
|
+
borderY: number;
|
|
415
|
+
};
|
|
411
416
|
showAllRegions(): void;
|
|
412
417
|
onDotplotView(mousedown: Coord, mouseup: Coord): void;
|
|
413
418
|
} & {
|
|
@@ -212,6 +212,8 @@ function stateModelFactory(pm) {
|
|
|
212
212
|
self.hview.zoomIn();
|
|
213
213
|
self.vview.zoomIn();
|
|
214
214
|
},
|
|
215
|
+
}))
|
|
216
|
+
.actions(self => ({
|
|
215
217
|
activateTrackSelector() {
|
|
216
218
|
if (self.trackSelectorType === 'hierarchical') {
|
|
217
219
|
const session = (0, util_1.getSession)(self);
|
|
@@ -289,7 +291,31 @@ function stateModelFactory(pm) {
|
|
|
289
291
|
self.vview.moveTo(y2, y1);
|
|
290
292
|
}
|
|
291
293
|
},
|
|
294
|
+
calculateBorders() {
|
|
295
|
+
if (self.volatileWidth === undefined) {
|
|
296
|
+
return { borderX: self.borderX, borderY: self.borderY };
|
|
297
|
+
}
|
|
298
|
+
const { vview, hview, viewHeight, viewWidth } = self;
|
|
299
|
+
const padding = 40;
|
|
300
|
+
const vblocks = vview.dynamicBlocks.contentBlocks;
|
|
301
|
+
const hblocks = hview.dynamicBlocks.contentBlocks;
|
|
302
|
+
const hoffset = hview.offsetPx;
|
|
303
|
+
const voffset = vview.offsetPx;
|
|
304
|
+
const vhide = (0, util_2.getBlockLabelKeysToHide)(vblocks, viewHeight, voffset);
|
|
305
|
+
const hhide = (0, util_2.getBlockLabelKeysToHide)(hblocks, viewWidth, hoffset);
|
|
306
|
+
const by = pxWidthForBlocks(hblocks, vview.bpPerPx, hhide);
|
|
307
|
+
const bx = pxWidthForBlocks(vblocks, hview.bpPerPx, vhide);
|
|
308
|
+
return {
|
|
309
|
+
borderX: Math.max(bx + padding, 50),
|
|
310
|
+
borderY: Math.max(by + padding, 50),
|
|
311
|
+
};
|
|
312
|
+
},
|
|
292
313
|
showAllRegions() {
|
|
314
|
+
self.hview.zoomTo(self.hview.maxBpPerPx);
|
|
315
|
+
self.vview.zoomTo(self.vview.maxBpPerPx);
|
|
316
|
+
const { borderX, borderY } = this.calculateBorders();
|
|
317
|
+
self.setBorderX(borderX);
|
|
318
|
+
self.setBorderY(borderY);
|
|
293
319
|
self.hview.zoomTo(self.hview.maxBpPerPx);
|
|
294
320
|
self.vview.zoomTo(self.vview.maxBpPerPx);
|
|
295
321
|
self.vview.center();
|
|
@@ -383,17 +409,15 @@ function stateModelFactory(pm) {
|
|
|
383
409
|
!self.assembliesInitialized) {
|
|
384
410
|
return;
|
|
385
411
|
}
|
|
386
|
-
|
|
387
|
-
|
|
412
|
+
const { hview, assemblyNames, vview } = self;
|
|
413
|
+
if (hview.displayedRegions.length &&
|
|
414
|
+
vview.displayedRegions.length) {
|
|
388
415
|
return;
|
|
389
416
|
}
|
|
390
|
-
const views = [self.hview, self.vview];
|
|
391
417
|
(0, mobx_1.transaction)(() => {
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
view.setDisplayedRegions((assembly === null || assembly === void 0 ? void 0 : assembly.regions) || []);
|
|
396
|
-
}
|
|
418
|
+
var _a, _b;
|
|
419
|
+
hview.setDisplayedRegions(((_a = session.assemblyManager.get(assemblyNames[0])) === null || _a === void 0 ? void 0 : _a.regions) || []);
|
|
420
|
+
vview.setDisplayedRegions(((_b = session.assemblyManager.get(assemblyNames[1])) === null || _b === void 0 ? void 0 : _b.regions) || []);
|
|
397
421
|
self.showAllRegions();
|
|
398
422
|
});
|
|
399
423
|
}, { delay: 1000 }));
|
|
@@ -401,18 +425,9 @@ function stateModelFactory(pm) {
|
|
|
401
425
|
if (self.volatileWidth === undefined) {
|
|
402
426
|
return;
|
|
403
427
|
}
|
|
404
|
-
const {
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
const hblocks = hview.dynamicBlocks.contentBlocks;
|
|
408
|
-
const hoffset = hview.offsetPx;
|
|
409
|
-
const voffset = vview.offsetPx;
|
|
410
|
-
const vhide = (0, util_2.getBlockLabelKeysToHide)(vblocks, viewHeight, voffset);
|
|
411
|
-
const hhide = (0, util_2.getBlockLabelKeysToHide)(hblocks, viewWidth, hoffset);
|
|
412
|
-
const by = pxWidthForBlocks(hblocks, vview.bpPerPx, hhide);
|
|
413
|
-
const bx = pxWidthForBlocks(vblocks, hview.bpPerPx, vhide);
|
|
414
|
-
self.setBorderY(Math.max(by + padding, 50));
|
|
415
|
-
self.setBorderX(Math.max(bx + padding, 50));
|
|
428
|
+
const { borderX, borderY } = self.calculateBorders();
|
|
429
|
+
self.setBorderX(borderX);
|
|
430
|
+
self.setBorderY(borderY);
|
|
416
431
|
}));
|
|
417
432
|
},
|
|
418
433
|
squareView() {
|
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
4
|
-
const react_1 = require("react");
|
|
5
4
|
const ui_1 = require("@jbrowse/core/ui");
|
|
6
|
-
const material_1 = require("@mui/material");
|
|
7
5
|
const mobx_react_1 = require("mobx-react");
|
|
8
6
|
const mui_1 = require("tss-react/mui");
|
|
9
7
|
const useStyles = (0, mui_1.makeStyles)()(theme => {
|
|
@@ -19,33 +17,18 @@ const useStyles = (0, mui_1.makeStyles)()(theme => {
|
|
|
19
17
|
backgroundColor: bg,
|
|
20
18
|
padding: '10px',
|
|
21
19
|
},
|
|
22
|
-
blockError: {
|
|
23
|
-
backgroundColor: bg,
|
|
24
|
-
padding: '10px',
|
|
25
|
-
color: 'red',
|
|
26
|
-
},
|
|
27
20
|
};
|
|
28
21
|
});
|
|
29
22
|
function LoadingMessage() {
|
|
30
|
-
const [shown, setShown] = (0, react_1.useState)(false);
|
|
31
23
|
const { classes } = useStyles();
|
|
32
|
-
(0,
|
|
33
|
-
const timeout = setTimeout(() => {
|
|
34
|
-
setShown(true);
|
|
35
|
-
}, 300);
|
|
36
|
-
return () => {
|
|
37
|
-
clearTimeout(timeout);
|
|
38
|
-
};
|
|
39
|
-
}, []);
|
|
40
|
-
return shown ? ((0, jsx_runtime_1.jsx)("div", { className: classes.loading, children: (0, jsx_runtime_1.jsx)(ui_1.LoadingEllipses, {}) })) : null;
|
|
24
|
+
return ((0, jsx_runtime_1.jsx)("div", { className: classes.loading, children: (0, jsx_runtime_1.jsx)(ui_1.LoadingEllipses, {}) }));
|
|
41
25
|
}
|
|
42
26
|
function BlockMessage({ messageText }) {
|
|
43
27
|
const { classes } = useStyles();
|
|
44
|
-
return ((0, jsx_runtime_1.jsx)("div", { className: classes.
|
|
28
|
+
return ((0, jsx_runtime_1.jsx)("div", { className: classes.loading, children: (0, jsx_runtime_1.jsx)(ui_1.LoadingEllipses, { message: messageText }) }));
|
|
45
29
|
}
|
|
46
30
|
function BlockError({ error }) {
|
|
47
|
-
|
|
48
|
-
return ((0, jsx_runtime_1.jsx)("div", { className: classes.blockError, children: (0, jsx_runtime_1.jsx)(material_1.Typography, { children: `${error}` }) }));
|
|
31
|
+
return (0, jsx_runtime_1.jsx)(ui_1.ErrorMessage, { error: error });
|
|
49
32
|
}
|
|
50
33
|
const ServerSideRenderedDotplotContent = (0, mobx_react_1.observer)(function ({ model, style, }) {
|
|
51
34
|
if (model.error) {
|
package/dist/index.js
CHANGED
|
@@ -7,6 +7,7 @@ const Plugin_1 = __importDefault(require("@jbrowse/core/Plugin"));
|
|
|
7
7
|
const util_1 = require("@jbrowse/core/util");
|
|
8
8
|
const Timeline_1 = __importDefault(require("@mui/icons-material/Timeline"));
|
|
9
9
|
const ComparativeRenderer_1 = __importDefault(require("./ComparativeRenderer"));
|
|
10
|
+
const DiagonalizeDotplotRpc_1 = __importDefault(require("./DiagonalizeDotplotRpc"));
|
|
10
11
|
const DotplotDisplay_1 = __importDefault(require("./DotplotDisplay"));
|
|
11
12
|
const DotplotReadVsRef_1 = __importDefault(require("./DotplotReadVsRef"));
|
|
12
13
|
const DotplotRenderer_1 = __importDefault(require("./DotplotRenderer"));
|
|
@@ -24,6 +25,7 @@ class DotplotPlugin extends Plugin_1.default {
|
|
|
24
25
|
(0, LaunchDotplotView_1.default)(pluginManager);
|
|
25
26
|
(0, DotplotReadVsRef_1.default)(pluginManager);
|
|
26
27
|
pluginManager.addRpcMethod(() => new ComparativeRenderer_1.default(pluginManager));
|
|
28
|
+
pluginManager.addRpcMethod(() => new DiagonalizeDotplotRpc_1.default(pluginManager));
|
|
27
29
|
}
|
|
28
30
|
configure(pluginManager) {
|
|
29
31
|
if ((0, util_1.isAbstractMenuManager)(pluginManager.rootModel)) {
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import RpcMethodTypeWithFiltersAndRenameRegions from '@jbrowse/core/pluggableElementTypes/RpcMethodTypeWithFiltersAndRenameRegions';
|
|
2
|
+
import type { Region } from '@jbrowse/core/util';
|
|
3
|
+
interface DiagonalizationResult {
|
|
4
|
+
newRegions: Region[];
|
|
5
|
+
stats: {
|
|
6
|
+
totalAlignments: number;
|
|
7
|
+
regionsProcessed: number;
|
|
8
|
+
regionsReordered: number;
|
|
9
|
+
regionsReversed: number;
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
interface DiagonalizeDotplotArgs {
|
|
13
|
+
sessionId: string;
|
|
14
|
+
view: {
|
|
15
|
+
hview: {
|
|
16
|
+
displayedRegions: Region[];
|
|
17
|
+
};
|
|
18
|
+
vview: {
|
|
19
|
+
displayedRegions: Region[];
|
|
20
|
+
};
|
|
21
|
+
};
|
|
22
|
+
adapterConfig: Record<string, unknown>;
|
|
23
|
+
stopToken?: string;
|
|
24
|
+
statusCallback?: (message: string) => void;
|
|
25
|
+
}
|
|
26
|
+
export default class DiagonalizeDotplotRpc extends RpcMethodTypeWithFiltersAndRenameRegions {
|
|
27
|
+
name: string;
|
|
28
|
+
execute(args: DiagonalizeDotplotArgs, rpcDriverClassName: string): Promise<DiagonalizationResult>;
|
|
29
|
+
}
|
|
30
|
+
export {};
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import RpcMethodTypeWithFiltersAndRenameRegions from '@jbrowse/core/pluggableElementTypes/RpcMethodTypeWithFiltersAndRenameRegions';
|
|
2
|
+
import { checkStopToken } from '@jbrowse/core/util/stopToken';
|
|
3
|
+
import { Dotplot1DView } from './DotplotView/model';
|
|
4
|
+
async function diagonalizeRegions(alignments, currentRegions, progressCallback) {
|
|
5
|
+
const updateProgress = (progress, message) => {
|
|
6
|
+
if (progressCallback) {
|
|
7
|
+
progressCallback(progress, message);
|
|
8
|
+
}
|
|
9
|
+
};
|
|
10
|
+
updateProgress(20, `Grouping ${alignments.length} alignments...`);
|
|
11
|
+
const queryGroups = new Map();
|
|
12
|
+
for (const aln of alignments) {
|
|
13
|
+
const targetRefName = aln.refRefName;
|
|
14
|
+
if (!queryGroups.has(targetRefName)) {
|
|
15
|
+
queryGroups.set(targetRefName, {
|
|
16
|
+
refAlignments: new Map(),
|
|
17
|
+
strandWeightedSum: 0,
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
const group = queryGroups.get(targetRefName);
|
|
21
|
+
const alnLength = Math.abs(aln.queryEnd - aln.queryStart);
|
|
22
|
+
if (!group.refAlignments.has(aln.queryRefName)) {
|
|
23
|
+
group.refAlignments.set(aln.queryRefName, {
|
|
24
|
+
bases: 0,
|
|
25
|
+
positions: [],
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
const refData = group.refAlignments.get(aln.queryRefName);
|
|
29
|
+
refData.bases += alnLength;
|
|
30
|
+
refData.positions.push((aln.queryStart + aln.queryEnd) / 2);
|
|
31
|
+
const direction = aln.strand >= 0 ? 1 : -1;
|
|
32
|
+
group.strandWeightedSum += direction * alnLength;
|
|
33
|
+
}
|
|
34
|
+
updateProgress(50, 'Determining optimal ordering and orientation...');
|
|
35
|
+
const queryOrdering = [];
|
|
36
|
+
for (const [targetRefName, group] of queryGroups) {
|
|
37
|
+
let bestRefName = '';
|
|
38
|
+
let maxBases = 0;
|
|
39
|
+
let bestPositions = [];
|
|
40
|
+
for (const [firstViewRefName, data] of group.refAlignments) {
|
|
41
|
+
if (data.bases > maxBases) {
|
|
42
|
+
maxBases = data.bases;
|
|
43
|
+
bestRefName = firstViewRefName;
|
|
44
|
+
bestPositions = data.positions;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
const bestRefPos = bestPositions.reduce((a, b) => a + b, 0) / bestPositions.length;
|
|
48
|
+
const shouldReverse = group.strandWeightedSum < 0;
|
|
49
|
+
queryOrdering.push({
|
|
50
|
+
refName: targetRefName,
|
|
51
|
+
bestRefName,
|
|
52
|
+
bestRefPos,
|
|
53
|
+
shouldReverse,
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
updateProgress(70, `Sorting ${queryOrdering.length} query regions...`);
|
|
57
|
+
queryOrdering.sort((a, b) => {
|
|
58
|
+
if (a.bestRefName !== b.bestRefName) {
|
|
59
|
+
return a.bestRefName.localeCompare(b.bestRefName);
|
|
60
|
+
}
|
|
61
|
+
return a.bestRefPos - b.bestRefPos;
|
|
62
|
+
});
|
|
63
|
+
updateProgress(85, 'Building new region layout...');
|
|
64
|
+
const newQueryRegions = [];
|
|
65
|
+
let regionsReversed = 0;
|
|
66
|
+
for (const { refName, shouldReverse } of queryOrdering) {
|
|
67
|
+
const region = currentRegions.find(r => r.refName === refName);
|
|
68
|
+
if (region) {
|
|
69
|
+
newQueryRegions.push({
|
|
70
|
+
...region,
|
|
71
|
+
reversed: shouldReverse,
|
|
72
|
+
});
|
|
73
|
+
if (shouldReverse !== region.reversed) {
|
|
74
|
+
regionsReversed++;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
const regionsWithoutAlignments = currentRegions.filter(r => !newQueryRegions.some(nr => nr.refName === r.refName));
|
|
79
|
+
updateProgress(100, 'Diagonalization complete!');
|
|
80
|
+
return {
|
|
81
|
+
newRegions: [...newQueryRegions, ...regionsWithoutAlignments],
|
|
82
|
+
stats: {
|
|
83
|
+
totalAlignments: alignments.length,
|
|
84
|
+
regionsProcessed: queryOrdering.length,
|
|
85
|
+
regionsReordered: newQueryRegions.length,
|
|
86
|
+
regionsReversed,
|
|
87
|
+
},
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
export default class DiagonalizeDotplotRpc extends RpcMethodTypeWithFiltersAndRenameRegions {
|
|
91
|
+
constructor() {
|
|
92
|
+
super(...arguments);
|
|
93
|
+
this.name = 'DiagonalizeDotplot';
|
|
94
|
+
}
|
|
95
|
+
async execute(args, rpcDriverClassName) {
|
|
96
|
+
const deserializedArgs = await this.deserializeArguments(args, rpcDriverClassName);
|
|
97
|
+
const { view, sessionId, adapterConfig, stopToken, statusCallback } = deserializedArgs;
|
|
98
|
+
if (!sessionId) {
|
|
99
|
+
throw new Error('must pass a unique session id');
|
|
100
|
+
}
|
|
101
|
+
statusCallback === null || statusCallback === void 0 ? void 0 : statusCallback('Initializing diagonalization...');
|
|
102
|
+
const dimensions = [800, 800];
|
|
103
|
+
const views = [view.hview, view.vview].map((snap, idx) => {
|
|
104
|
+
const v = Dotplot1DView.create(snap);
|
|
105
|
+
v.setVolatileWidth(dimensions[idx]);
|
|
106
|
+
return v;
|
|
107
|
+
});
|
|
108
|
+
const targetView = views[0];
|
|
109
|
+
checkStopToken(stopToken);
|
|
110
|
+
statusCallback === null || statusCallback === void 0 ? void 0 : statusCallback('Getting renderer...');
|
|
111
|
+
const rendererType = this.pluginManager.getRendererType('DotplotRenderer');
|
|
112
|
+
if (!rendererType) {
|
|
113
|
+
throw new Error('DotplotRenderer not found');
|
|
114
|
+
}
|
|
115
|
+
checkStopToken(stopToken);
|
|
116
|
+
statusCallback === null || statusCallback === void 0 ? void 0 : statusCallback('Fetching features...');
|
|
117
|
+
const feats = await rendererType.getFeatures({
|
|
118
|
+
regions: targetView.dynamicBlocks.contentBlocks,
|
|
119
|
+
adapterConfig,
|
|
120
|
+
sessionId,
|
|
121
|
+
});
|
|
122
|
+
checkStopToken(stopToken);
|
|
123
|
+
statusCallback === null || statusCallback === void 0 ? void 0 : statusCallback('Extracting alignment data...');
|
|
124
|
+
const alignments = [];
|
|
125
|
+
for (const feat of feats.values()) {
|
|
126
|
+
const mate = feat.get('mate');
|
|
127
|
+
if (mate) {
|
|
128
|
+
alignments.push({
|
|
129
|
+
queryRefName: feat.get('refName'),
|
|
130
|
+
refRefName: mate.refName,
|
|
131
|
+
queryStart: feat.get('start'),
|
|
132
|
+
queryEnd: feat.get('end'),
|
|
133
|
+
refStart: mate.start,
|
|
134
|
+
refEnd: mate.end,
|
|
135
|
+
strand: feat.get('strand') || 1,
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
if (alignments.length === 0) {
|
|
140
|
+
throw new Error('No alignments found to diagonalize');
|
|
141
|
+
}
|
|
142
|
+
statusCallback === null || statusCallback === void 0 ? void 0 : statusCallback(`Running diagonalization on ${alignments.length} alignments...`);
|
|
143
|
+
const result = await diagonalizeRegions(alignments, view.vview.displayedRegions, (progress, message) => {
|
|
144
|
+
checkStopToken(stopToken);
|
|
145
|
+
statusCallback === null || statusCallback === void 0 ? void 0 : statusCallback(message);
|
|
146
|
+
});
|
|
147
|
+
statusCallback === null || statusCallback === void 0 ? void 0 : statusCallback('Diagonalization complete!');
|
|
148
|
+
return result;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
@@ -9,6 +9,7 @@ export declare function stateModelFactory(configSchema: AnyConfigurationSchemaTy
|
|
|
9
9
|
} & {
|
|
10
10
|
type: import("mobx-state-tree").ISimpleType<"DotplotDisplay">;
|
|
11
11
|
configuration: AnyConfigurationSchemaType;
|
|
12
|
+
colorBy: import("mobx-state-tree").IOptionalIType<import("mobx-state-tree").ISimpleType<string>, [undefined]>;
|
|
12
13
|
}, {
|
|
13
14
|
rendererTypeName: string;
|
|
14
15
|
error: unknown;
|
|
@@ -79,6 +80,8 @@ export declare function stateModelFactory(configSchema: AnyConfigurationSchemaTy
|
|
|
79
80
|
message: string | undefined;
|
|
80
81
|
renderingComponent: any;
|
|
81
82
|
ReactComponent2: React.FC<any>;
|
|
83
|
+
alpha: number;
|
|
84
|
+
minAlignmentLength: number;
|
|
82
85
|
} & {
|
|
83
86
|
readonly shouldDisplay: boolean;
|
|
84
87
|
readonly rendererTypeName: any;
|
|
@@ -97,6 +100,9 @@ export declare function stateModelFactory(configSchema: AnyConfigurationSchemaTy
|
|
|
97
100
|
renderingComponent: React.Component;
|
|
98
101
|
}): void;
|
|
99
102
|
setError(error: unknown): void;
|
|
103
|
+
setAlpha(value: number): void;
|
|
104
|
+
setMinAlignmentLength(value: number): void;
|
|
105
|
+
setColorBy(value: string): void;
|
|
100
106
|
}, import("mobx-state-tree")._NotCustomized, import("mobx-state-tree")._NotCustomized>;
|
|
101
107
|
export type DotplotDisplayStateModel = ReturnType<typeof stateModelFactory>;
|
|
102
108
|
export type DotplotDisplayModel = Instance<DotplotDisplayStateModel>;
|
|
@@ -12,6 +12,7 @@ export function stateModelFactory(configSchema) {
|
|
|
12
12
|
.model({
|
|
13
13
|
type: types.literal('DotplotDisplay'),
|
|
14
14
|
configuration: ConfigurationReference(configSchema),
|
|
15
|
+
colorBy: types.optional(types.string, 'default'),
|
|
15
16
|
})
|
|
16
17
|
.volatile(() => ({
|
|
17
18
|
stopToken: undefined,
|
|
@@ -22,6 +23,8 @@ export function stateModelFactory(configSchema) {
|
|
|
22
23
|
message: undefined,
|
|
23
24
|
renderingComponent: undefined,
|
|
24
25
|
ReactComponent2: ServerSideRenderedBlockContent,
|
|
26
|
+
alpha: 1,
|
|
27
|
+
minAlignmentLength: 0,
|
|
25
28
|
})))
|
|
26
29
|
.views(self => ({
|
|
27
30
|
get shouldDisplay() {
|
|
@@ -38,6 +41,9 @@ export function stateModelFactory(configSchema) {
|
|
|
38
41
|
rpcDriverName: self.rpcDriverName,
|
|
39
42
|
displayModel: self,
|
|
40
43
|
config: self.configuration.renderer,
|
|
44
|
+
statusCallback: (message) => {
|
|
45
|
+
self.setMessage(message);
|
|
46
|
+
},
|
|
41
47
|
};
|
|
42
48
|
},
|
|
43
49
|
}))
|
|
@@ -109,5 +115,14 @@ export function stateModelFactory(configSchema) {
|
|
|
109
115
|
self.renderingComponent = undefined;
|
|
110
116
|
self.stopToken = undefined;
|
|
111
117
|
},
|
|
118
|
+
setAlpha(value) {
|
|
119
|
+
self.alpha = value;
|
|
120
|
+
},
|
|
121
|
+
setMinAlignmentLength(value) {
|
|
122
|
+
self.minAlignmentLength = value;
|
|
123
|
+
},
|
|
124
|
+
setColorBy(value) {
|
|
125
|
+
self.colorBy = value;
|
|
126
|
+
},
|
|
112
127
|
}));
|
|
113
128
|
}
|
|
@@ -35,10 +35,7 @@ export default class DotplotRenderer extends ComparativeRenderer {
|
|
|
35
35
|
offsetY: number;
|
|
36
36
|
bpPerPxX: number;
|
|
37
37
|
bpPerPxY: number;
|
|
38
|
-
warnings:
|
|
39
|
-
message: string;
|
|
40
|
-
effect: string;
|
|
41
|
-
}[];
|
|
38
|
+
warnings: import("./clamp").Warning[];
|
|
42
39
|
canvasRecordedData: Record<string, unknown>;
|
|
43
40
|
reactElement?: React.ReactElement;
|
|
44
41
|
html?: string;
|
|
@@ -49,10 +46,7 @@ export default class DotplotRenderer extends ComparativeRenderer {
|
|
|
49
46
|
offsetY: number;
|
|
50
47
|
bpPerPxX: number;
|
|
51
48
|
bpPerPxY: number;
|
|
52
|
-
warnings:
|
|
53
|
-
message: string;
|
|
54
|
-
effect: string;
|
|
55
|
-
}[];
|
|
49
|
+
warnings: import("./clamp").Warning[];
|
|
56
50
|
imageData: any;
|
|
57
51
|
reactElement?: React.ReactElement;
|
|
58
52
|
html?: string;
|
|
@@ -63,10 +57,7 @@ export default class DotplotRenderer extends ComparativeRenderer {
|
|
|
63
57
|
offsetY: number;
|
|
64
58
|
bpPerPxX: number;
|
|
65
59
|
bpPerPxY: number;
|
|
66
|
-
warnings:
|
|
67
|
-
message: string;
|
|
68
|
-
effect: string;
|
|
69
|
-
}[];
|
|
60
|
+
warnings: import("./clamp").Warning[];
|
|
70
61
|
reactElement: React.ReactElement;
|
|
71
62
|
html?: string;
|
|
72
63
|
}>;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { Feature } from '@jbrowse/core/util';
|
|
2
|
+
export declare function clampWithWarnX(num: number, min: number, max: number, feature: Feature, warnings: Warning[]): number;
|
|
3
|
+
export declare function clampWithWarnY(num: number, min: number, max: number, feature: Feature, warnings: Warning[]): number;
|
|
4
|
+
export interface Warning {
|
|
5
|
+
message: string;
|
|
6
|
+
effect: string;
|
|
7
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
const r = 'fell outside of range due to CIGAR string';
|
|
2
|
+
const lt = '(less than min coordinate of feature)';
|
|
3
|
+
const gt = '(greater than max coordinate of feature)';
|
|
4
|
+
const fudgeFactor = 1;
|
|
5
|
+
export function clampWithWarnX(num, min, max, feature, warnings) {
|
|
6
|
+
const strand = feature.get('strand') || 1;
|
|
7
|
+
if (strand === -1) {
|
|
8
|
+
;
|
|
9
|
+
[max, min] = [min, max];
|
|
10
|
+
}
|
|
11
|
+
if (num < min - fudgeFactor) {
|
|
12
|
+
let start = feature.get('start');
|
|
13
|
+
let end = feature.get('end');
|
|
14
|
+
const refName = feature.get('refName');
|
|
15
|
+
if (strand === -1) {
|
|
16
|
+
;
|
|
17
|
+
[end, start] = [start, end];
|
|
18
|
+
}
|
|
19
|
+
warnings.push({
|
|
20
|
+
message: `feature at (X ${refName}:${start}-${end}) ${r} ${lt}`,
|
|
21
|
+
effect: 'clipped the feature',
|
|
22
|
+
});
|
|
23
|
+
return min;
|
|
24
|
+
}
|
|
25
|
+
if (num > max + fudgeFactor) {
|
|
26
|
+
const strand = feature.get('strand') || 1;
|
|
27
|
+
const start = strand === 1 ? feature.get('start') : feature.get('end');
|
|
28
|
+
const end = strand === 1 ? feature.get('end') : feature.get('start');
|
|
29
|
+
const refName = feature.get('refName');
|
|
30
|
+
warnings.push({
|
|
31
|
+
message: `feature at (X ${refName}:${start}-${end}) ${r} ${gt}`,
|
|
32
|
+
effect: 'clipped the feature',
|
|
33
|
+
});
|
|
34
|
+
return max;
|
|
35
|
+
}
|
|
36
|
+
return num;
|
|
37
|
+
}
|
|
38
|
+
export function clampWithWarnY(num, min, max, feature, warnings) {
|
|
39
|
+
if (num < min - fudgeFactor) {
|
|
40
|
+
const mate = feature.get('mate');
|
|
41
|
+
const { refName, start, end } = mate;
|
|
42
|
+
warnings.push({
|
|
43
|
+
message: `feature at (Y ${refName}:${start}-${end}) ${r} ${lt}`,
|
|
44
|
+
effect: 'clipped the feature',
|
|
45
|
+
});
|
|
46
|
+
return min;
|
|
47
|
+
}
|
|
48
|
+
if (num > max + fudgeFactor) {
|
|
49
|
+
const mate = feature.get('mate');
|
|
50
|
+
const { refName, start, end } = mate;
|
|
51
|
+
warnings.push({
|
|
52
|
+
message: `feature at (Y ${refName}:${start}-${end}) ${r} ${gt}`,
|
|
53
|
+
effect: 'clipped the feature',
|
|
54
|
+
});
|
|
55
|
+
return max;
|
|
56
|
+
}
|
|
57
|
+
return num;
|
|
58
|
+
}
|