@jbrowse/plugin-dotplot-view 3.6.5 → 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.
Files changed (97) hide show
  1. package/dist/DiagonalizeDotplotRpc.d.ts +30 -0
  2. package/dist/DiagonalizeDotplotRpc.js +156 -0
  3. package/dist/DotplotDisplay/renderDotplotBlock.js +3 -0
  4. package/dist/DotplotDisplay/stateModelFactory.d.ts +6 -0
  5. package/dist/DotplotDisplay/stateModelFactory.js +15 -0
  6. package/dist/DotplotRenderer/DotplotRenderer.d.ts +3 -12
  7. package/dist/DotplotRenderer/clamp.d.ts +7 -0
  8. package/dist/DotplotRenderer/clamp.js +62 -0
  9. package/dist/DotplotRenderer/drawDotplot.d.ts +5 -4
  10. package/dist/DotplotRenderer/drawDotplot.js +92 -96
  11. package/dist/DotplotView/1dview.js +5 -3
  12. package/dist/DotplotView/components/ColorBySelector.d.ts +5 -0
  13. package/dist/DotplotView/components/ColorBySelector.js +79 -0
  14. package/dist/DotplotView/components/DiagonalizationProgressDialog.d.ts +6 -0
  15. package/dist/DotplotView/components/DiagonalizationProgressDialog.js +125 -0
  16. package/dist/DotplotView/components/DotplotControls.js +84 -12
  17. package/dist/DotplotView/components/DotplotTooltips.d.ts +15 -0
  18. package/dist/DotplotView/components/DotplotTooltips.js +43 -0
  19. package/dist/DotplotView/components/DotplotView.js +16 -191
  20. package/dist/DotplotView/components/DotplotWarnings.js +3 -3
  21. package/dist/DotplotView/components/ImportForm/index.js +0 -1
  22. package/dist/DotplotView/components/MinLengthSlider.d.ts +5 -0
  23. package/dist/DotplotView/components/MinLengthSlider.js +44 -0
  24. package/dist/DotplotView/components/MouseInteractionLayer.d.ts +17 -0
  25. package/dist/DotplotView/components/MouseInteractionLayer.js +18 -0
  26. package/dist/DotplotView/components/OpacitySlider.d.ts +5 -0
  27. package/dist/DotplotView/components/OpacitySlider.js +43 -0
  28. package/dist/DotplotView/components/SelectionContextMenu.d.ts +13 -0
  29. package/dist/DotplotView/components/SelectionContextMenu.js +42 -0
  30. package/dist/DotplotView/components/SliderTooltip.d.ts +2 -0
  31. package/dist/DotplotView/components/SliderTooltip.js +9 -0
  32. package/dist/DotplotView/components/hooks/useCtrlKeyTracking.d.ts +1 -0
  33. package/dist/DotplotView/components/hooks/useCtrlKeyTracking.js +24 -0
  34. package/dist/DotplotView/components/hooks/useCursorMode.d.ts +7 -0
  35. package/dist/DotplotView/components/hooks/useCursorMode.js +19 -0
  36. package/dist/DotplotView/components/hooks/useMouseCoordinates.d.ts +29 -0
  37. package/dist/DotplotView/components/hooks/useMouseCoordinates.js +52 -0
  38. package/dist/DotplotView/components/hooks/useMouseMoveHandler.d.ts +6 -0
  39. package/dist/DotplotView/components/hooks/useMouseMoveHandler.js +27 -0
  40. package/dist/DotplotView/components/hooks/useMouseUpHandler.d.ts +3 -0
  41. package/dist/DotplotView/components/hooks/useMouseUpHandler.js +31 -0
  42. package/dist/DotplotView/components/hooks/useWheelHandler.d.ts +8 -0
  43. package/dist/DotplotView/components/hooks/useWheelHandler.js +47 -0
  44. package/dist/DotplotView/components/util.js +1 -3
  45. package/dist/DotplotView/model.d.ts +5 -0
  46. package/dist/DotplotView/model.js +35 -20
  47. package/dist/ServerSideRenderedBlockContent.js +3 -20
  48. package/dist/index.js +2 -0
  49. package/esm/DiagonalizeDotplotRpc.d.ts +30 -0
  50. package/esm/DiagonalizeDotplotRpc.js +150 -0
  51. package/esm/DotplotDisplay/renderDotplotBlock.js +3 -0
  52. package/esm/DotplotDisplay/stateModelFactory.d.ts +6 -0
  53. package/esm/DotplotDisplay/stateModelFactory.js +15 -0
  54. package/esm/DotplotRenderer/DotplotRenderer.d.ts +3 -12
  55. package/esm/DotplotRenderer/clamp.d.ts +7 -0
  56. package/esm/DotplotRenderer/clamp.js +58 -0
  57. package/esm/DotplotRenderer/drawDotplot.d.ts +5 -4
  58. package/esm/DotplotRenderer/drawDotplot.js +92 -96
  59. package/esm/DotplotView/1dview.js +5 -3
  60. package/esm/DotplotView/components/ColorBySelector.d.ts +5 -0
  61. package/esm/DotplotView/components/ColorBySelector.js +74 -0
  62. package/esm/DotplotView/components/DiagonalizationProgressDialog.d.ts +6 -0
  63. package/esm/DotplotView/components/DiagonalizationProgressDialog.js +123 -0
  64. package/esm/DotplotView/components/DotplotControls.js +52 -13
  65. package/esm/DotplotView/components/DotplotTooltips.d.ts +15 -0
  66. package/esm/DotplotView/components/DotplotTooltips.js +7 -0
  67. package/esm/DotplotView/components/DotplotView.js +17 -159
  68. package/esm/DotplotView/components/DotplotWarnings.js +4 -4
  69. package/esm/DotplotView/components/ImportForm/index.js +0 -1
  70. package/esm/DotplotView/components/MinLengthSlider.d.ts +5 -0
  71. package/esm/DotplotView/components/MinLengthSlider.js +39 -0
  72. package/esm/DotplotView/components/MouseInteractionLayer.d.ts +17 -0
  73. package/esm/DotplotView/components/MouseInteractionLayer.js +12 -0
  74. package/esm/DotplotView/components/OpacitySlider.d.ts +5 -0
  75. package/esm/DotplotView/components/OpacitySlider.js +38 -0
  76. package/esm/DotplotView/components/SelectionContextMenu.d.ts +13 -0
  77. package/esm/DotplotView/components/SelectionContextMenu.js +39 -0
  78. package/esm/DotplotView/components/SliderTooltip.d.ts +2 -0
  79. package/esm/DotplotView/components/SliderTooltip.js +6 -0
  80. package/esm/DotplotView/components/hooks/useCtrlKeyTracking.d.ts +1 -0
  81. package/esm/DotplotView/components/hooks/useCtrlKeyTracking.js +21 -0
  82. package/esm/DotplotView/components/hooks/useCursorMode.d.ts +7 -0
  83. package/esm/DotplotView/components/hooks/useCursorMode.js +16 -0
  84. package/esm/DotplotView/components/hooks/useMouseCoordinates.d.ts +29 -0
  85. package/esm/DotplotView/components/hooks/useMouseCoordinates.js +49 -0
  86. package/esm/DotplotView/components/hooks/useMouseMoveHandler.d.ts +6 -0
  87. package/esm/DotplotView/components/hooks/useMouseMoveHandler.js +24 -0
  88. package/esm/DotplotView/components/hooks/useMouseUpHandler.d.ts +3 -0
  89. package/esm/DotplotView/components/hooks/useMouseUpHandler.js +28 -0
  90. package/esm/DotplotView/components/hooks/useWheelHandler.d.ts +8 -0
  91. package/esm/DotplotView/components/hooks/useWheelHandler.js +44 -0
  92. package/esm/DotplotView/components/util.js +1 -3
  93. package/esm/DotplotView/model.d.ts +5 -0
  94. package/esm/DotplotView/model.js +35 -20
  95. package/esm/ServerSideRenderedBlockContent.js +4 -21
  96. package/esm/index.js +2 -0
  97. 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 = +Number(minMajorPitchBp)
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
- if (self.hview.displayedRegions.length > 0 &&
387
- self.vview.displayedRegions.length > 0) {
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
- for (const [index, name] of self.assemblyNames.entries()) {
393
- const assembly = session.assemblyManager.get(name);
394
- const view = views[index];
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 { vview, hview, viewHeight, viewWidth } = self;
405
- const padding = 40;
406
- const vblocks = vview.dynamicBlocks.contentBlocks;
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, react_1.useEffect)(() => {
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.blockMessage, children: (0, jsx_runtime_1.jsx)(material_1.Typography, { children: messageText }) }));
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
- const { classes } = useStyles();
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
+ }
@@ -26,6 +26,9 @@ export function renderBlockData(self) {
26
26
  rendererType: rendererType.name,
27
27
  sessionId: getRpcSessionId(self),
28
28
  timeout: 1000000,
29
+ alpha: self.alpha,
30
+ minAlignmentLength: self.minAlignmentLength,
31
+ colorBy: self.colorBy,
29
32
  },
30
33
  };
31
34
  }
@@ -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
+ }