@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.
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,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,156 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const RpcMethodTypeWithFiltersAndRenameRegions_1 = __importDefault(require("@jbrowse/core/pluggableElementTypes/RpcMethodTypeWithFiltersAndRenameRegions"));
7
+ const stopToken_1 = require("@jbrowse/core/util/stopToken");
8
+ const model_1 = require("./DotplotView/model");
9
+ async function diagonalizeRegions(alignments, currentRegions, progressCallback) {
10
+ const updateProgress = (progress, message) => {
11
+ if (progressCallback) {
12
+ progressCallback(progress, message);
13
+ }
14
+ };
15
+ updateProgress(20, `Grouping ${alignments.length} alignments...`);
16
+ const queryGroups = new Map();
17
+ for (const aln of alignments) {
18
+ const targetRefName = aln.refRefName;
19
+ if (!queryGroups.has(targetRefName)) {
20
+ queryGroups.set(targetRefName, {
21
+ refAlignments: new Map(),
22
+ strandWeightedSum: 0,
23
+ });
24
+ }
25
+ const group = queryGroups.get(targetRefName);
26
+ const alnLength = Math.abs(aln.queryEnd - aln.queryStart);
27
+ if (!group.refAlignments.has(aln.queryRefName)) {
28
+ group.refAlignments.set(aln.queryRefName, {
29
+ bases: 0,
30
+ positions: [],
31
+ });
32
+ }
33
+ const refData = group.refAlignments.get(aln.queryRefName);
34
+ refData.bases += alnLength;
35
+ refData.positions.push((aln.queryStart + aln.queryEnd) / 2);
36
+ const direction = aln.strand >= 0 ? 1 : -1;
37
+ group.strandWeightedSum += direction * alnLength;
38
+ }
39
+ updateProgress(50, 'Determining optimal ordering and orientation...');
40
+ const queryOrdering = [];
41
+ for (const [targetRefName, group] of queryGroups) {
42
+ let bestRefName = '';
43
+ let maxBases = 0;
44
+ let bestPositions = [];
45
+ for (const [firstViewRefName, data] of group.refAlignments) {
46
+ if (data.bases > maxBases) {
47
+ maxBases = data.bases;
48
+ bestRefName = firstViewRefName;
49
+ bestPositions = data.positions;
50
+ }
51
+ }
52
+ const bestRefPos = bestPositions.reduce((a, b) => a + b, 0) / bestPositions.length;
53
+ const shouldReverse = group.strandWeightedSum < 0;
54
+ queryOrdering.push({
55
+ refName: targetRefName,
56
+ bestRefName,
57
+ bestRefPos,
58
+ shouldReverse,
59
+ });
60
+ }
61
+ updateProgress(70, `Sorting ${queryOrdering.length} query regions...`);
62
+ queryOrdering.sort((a, b) => {
63
+ if (a.bestRefName !== b.bestRefName) {
64
+ return a.bestRefName.localeCompare(b.bestRefName);
65
+ }
66
+ return a.bestRefPos - b.bestRefPos;
67
+ });
68
+ updateProgress(85, 'Building new region layout...');
69
+ const newQueryRegions = [];
70
+ let regionsReversed = 0;
71
+ for (const { refName, shouldReverse } of queryOrdering) {
72
+ const region = currentRegions.find(r => r.refName === refName);
73
+ if (region) {
74
+ newQueryRegions.push({
75
+ ...region,
76
+ reversed: shouldReverse,
77
+ });
78
+ if (shouldReverse !== region.reversed) {
79
+ regionsReversed++;
80
+ }
81
+ }
82
+ }
83
+ const regionsWithoutAlignments = currentRegions.filter(r => !newQueryRegions.some(nr => nr.refName === r.refName));
84
+ updateProgress(100, 'Diagonalization complete!');
85
+ return {
86
+ newRegions: [...newQueryRegions, ...regionsWithoutAlignments],
87
+ stats: {
88
+ totalAlignments: alignments.length,
89
+ regionsProcessed: queryOrdering.length,
90
+ regionsReordered: newQueryRegions.length,
91
+ regionsReversed,
92
+ },
93
+ };
94
+ }
95
+ class DiagonalizeDotplotRpc extends RpcMethodTypeWithFiltersAndRenameRegions_1.default {
96
+ constructor() {
97
+ super(...arguments);
98
+ this.name = 'DiagonalizeDotplot';
99
+ }
100
+ async execute(args, rpcDriverClassName) {
101
+ const deserializedArgs = await this.deserializeArguments(args, rpcDriverClassName);
102
+ const { view, sessionId, adapterConfig, stopToken, statusCallback } = deserializedArgs;
103
+ if (!sessionId) {
104
+ throw new Error('must pass a unique session id');
105
+ }
106
+ statusCallback === null || statusCallback === void 0 ? void 0 : statusCallback('Initializing diagonalization...');
107
+ const dimensions = [800, 800];
108
+ const views = [view.hview, view.vview].map((snap, idx) => {
109
+ const v = model_1.Dotplot1DView.create(snap);
110
+ v.setVolatileWidth(dimensions[idx]);
111
+ return v;
112
+ });
113
+ const targetView = views[0];
114
+ (0, stopToken_1.checkStopToken)(stopToken);
115
+ statusCallback === null || statusCallback === void 0 ? void 0 : statusCallback('Getting renderer...');
116
+ const rendererType = this.pluginManager.getRendererType('DotplotRenderer');
117
+ if (!rendererType) {
118
+ throw new Error('DotplotRenderer not found');
119
+ }
120
+ (0, stopToken_1.checkStopToken)(stopToken);
121
+ statusCallback === null || statusCallback === void 0 ? void 0 : statusCallback('Fetching features...');
122
+ const feats = await rendererType.getFeatures({
123
+ regions: targetView.dynamicBlocks.contentBlocks,
124
+ adapterConfig,
125
+ sessionId,
126
+ });
127
+ (0, stopToken_1.checkStopToken)(stopToken);
128
+ statusCallback === null || statusCallback === void 0 ? void 0 : statusCallback('Extracting alignment data...');
129
+ const alignments = [];
130
+ for (const feat of feats.values()) {
131
+ const mate = feat.get('mate');
132
+ if (mate) {
133
+ alignments.push({
134
+ queryRefName: feat.get('refName'),
135
+ refRefName: mate.refName,
136
+ queryStart: feat.get('start'),
137
+ queryEnd: feat.get('end'),
138
+ refStart: mate.start,
139
+ refEnd: mate.end,
140
+ strand: feat.get('strand') || 1,
141
+ });
142
+ }
143
+ }
144
+ if (alignments.length === 0) {
145
+ throw new Error('No alignments found to diagonalize');
146
+ }
147
+ statusCallback === null || statusCallback === void 0 ? void 0 : statusCallback(`Running diagonalization on ${alignments.length} alignments...`);
148
+ const result = await diagonalizeRegions(alignments, view.vview.displayedRegions, (progress, message) => {
149
+ (0, stopToken_1.checkStopToken)(stopToken);
150
+ statusCallback === null || statusCallback === void 0 ? void 0 : statusCallback(message);
151
+ });
152
+ statusCallback === null || statusCallback === void 0 ? void 0 : statusCallback('Diagonalization complete!');
153
+ return result;
154
+ }
155
+ }
156
+ exports.default = DiagonalizeDotplotRpc;
@@ -30,6 +30,9 @@ function renderBlockData(self) {
30
30
  rendererType: rendererType.name,
31
31
  sessionId: (0, tracks_1.getRpcSessionId)(self),
32
32
  timeout: 1000000,
33
+ alpha: self.alpha,
34
+ minAlignmentLength: self.minAlignmentLength,
35
+ colorBy: self.colorBy,
33
36
  },
34
37
  };
35
38
  }
@@ -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>;
@@ -18,6 +18,7 @@ function stateModelFactory(configSchema) {
18
18
  .model({
19
19
  type: mobx_state_tree_1.types.literal('DotplotDisplay'),
20
20
  configuration: (0, configuration_1.ConfigurationReference)(configSchema),
21
+ colorBy: mobx_state_tree_1.types.optional(mobx_state_tree_1.types.string, 'default'),
21
22
  })
22
23
  .volatile(() => ({
23
24
  stopToken: undefined,
@@ -28,6 +29,8 @@ function stateModelFactory(configSchema) {
28
29
  message: undefined,
29
30
  renderingComponent: undefined,
30
31
  ReactComponent2: ServerSideRenderedBlockContent_1.default,
32
+ alpha: 1,
33
+ minAlignmentLength: 0,
31
34
  })))
32
35
  .views(self => ({
33
36
  get shouldDisplay() {
@@ -44,6 +47,9 @@ function stateModelFactory(configSchema) {
44
47
  rpcDriverName: self.rpcDriverName,
45
48
  displayModel: self,
46
49
  config: self.configuration.renderer,
50
+ statusCallback: (message) => {
51
+ self.setMessage(message);
52
+ },
47
53
  };
48
54
  },
49
55
  }))
@@ -115,5 +121,14 @@ function stateModelFactory(configSchema) {
115
121
  self.renderingComponent = undefined;
116
122
  self.stopToken = undefined;
117
123
  },
124
+ setAlpha(value) {
125
+ self.alpha = value;
126
+ },
127
+ setMinAlignmentLength(value) {
128
+ self.minAlignmentLength = value;
129
+ },
130
+ setColorBy(value) {
131
+ self.colorBy = value;
132
+ },
118
133
  }));
119
134
  }
@@ -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,62 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.clampWithWarnX = clampWithWarnX;
4
+ exports.clampWithWarnY = clampWithWarnY;
5
+ const r = 'fell outside of range due to CIGAR string';
6
+ const lt = '(less than min coordinate of feature)';
7
+ const gt = '(greater than max coordinate of feature)';
8
+ const fudgeFactor = 1;
9
+ function clampWithWarnX(num, min, max, feature, warnings) {
10
+ const strand = feature.get('strand') || 1;
11
+ if (strand === -1) {
12
+ ;
13
+ [max, min] = [min, max];
14
+ }
15
+ if (num < min - fudgeFactor) {
16
+ let start = feature.get('start');
17
+ let end = feature.get('end');
18
+ const refName = feature.get('refName');
19
+ if (strand === -1) {
20
+ ;
21
+ [end, start] = [start, end];
22
+ }
23
+ warnings.push({
24
+ message: `feature at (X ${refName}:${start}-${end}) ${r} ${lt}`,
25
+ effect: 'clipped the feature',
26
+ });
27
+ return min;
28
+ }
29
+ if (num > max + fudgeFactor) {
30
+ const strand = feature.get('strand') || 1;
31
+ const start = strand === 1 ? feature.get('start') : feature.get('end');
32
+ const end = strand === 1 ? feature.get('end') : feature.get('start');
33
+ const refName = feature.get('refName');
34
+ warnings.push({
35
+ message: `feature at (X ${refName}:${start}-${end}) ${r} ${gt}`,
36
+ effect: 'clipped the feature',
37
+ });
38
+ return max;
39
+ }
40
+ return num;
41
+ }
42
+ function clampWithWarnY(num, min, max, feature, warnings) {
43
+ if (num < min - fudgeFactor) {
44
+ const mate = feature.get('mate');
45
+ const { refName, start, end } = mate;
46
+ warnings.push({
47
+ message: `feature at (Y ${refName}:${start}-${end}) ${r} ${lt}`,
48
+ effect: 'clipped the feature',
49
+ });
50
+ return min;
51
+ }
52
+ if (num > max + fudgeFactor) {
53
+ const mate = feature.get('mate');
54
+ const { refName, start, end } = mate;
55
+ warnings.push({
56
+ message: `feature at (Y ${refName}:${start}-${end}) ${r} ${gt}`,
57
+ effect: 'clipped the feature',
58
+ });
59
+ return max;
60
+ }
61
+ return num;
62
+ }
@@ -1,3 +1,4 @@
1
+ import { type Warning } from './clamp';
1
2
  import type { Dotplot1DViewModel } from '../DotplotView/model';
2
3
  import type { AnyConfigurationModel } from '@jbrowse/core/configuration';
3
4
  import type { RenderArgsDeserialized } from '@jbrowse/core/pluggableElementTypes/renderers/ComparativeServerSideRendererType';
@@ -6,6 +7,9 @@ export interface DotplotRenderArgsDeserialized extends RenderArgsDeserialized {
6
7
  height: number;
7
8
  width: number;
8
9
  highResolutionScaling: number;
10
+ alpha?: number;
11
+ minAlignmentLength?: number;
12
+ colorBy?: string;
9
13
  view: {
10
14
  hview: Dotplot1DViewModel;
11
15
  vview: Dotplot1DViewModel;
@@ -14,8 +18,5 @@ export interface DotplotRenderArgsDeserialized extends RenderArgsDeserialized {
14
18
  export declare function drawDotplot(ctx: CanvasRenderingContext2D, props: DotplotRenderArgsDeserialized & {
15
19
  views: Dotplot1DViewModel[];
16
20
  }): Promise<{
17
- warnings: {
18
- message: string;
19
- effect: string;
20
- }[];
21
+ warnings: Warning[];
21
22
  }>;
@@ -3,26 +3,39 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.drawDotplot = drawDotplot;
4
4
  const configuration_1 = require("@jbrowse/core/configuration");
5
5
  const ui_1 = require("@jbrowse/core/ui");
6
+ const colors_1 = require("@jbrowse/core/ui/colors");
6
7
  const Base1DUtils_1 = require("@jbrowse/core/util/Base1DUtils");
8
+ const colord_1 = require("@jbrowse/core/util/colord");
7
9
  const plugin_alignments_1 = require("@jbrowse/plugin-alignments");
8
10
  const mobx_state_tree_1 = require("mobx-state-tree");
11
+ const clamp_1 = require("./clamp");
9
12
  const { parseCigar } = plugin_alignments_1.MismatchParser;
10
- const r = 'fell outside of range due to CIGAR string';
11
- const lt = '(less than min coordinate of feature)';
12
- const gt = '(greater than max coordinate of feature)';
13
- const fudgeFactor = 1;
14
- function drawCir(ctx, x, y, r = 1) {
15
- ctx.beginPath();
16
- ctx.arc(x, y, r / 2, 0, 2 * Math.PI);
17
- ctx.fill();
13
+ function hashString(str) {
14
+ let hash = 0;
15
+ for (let i = 0; i < str.length; i++) {
16
+ const char = str.charCodeAt(i);
17
+ hash = (hash << 5) - hash + char;
18
+ hash = hash & hash;
19
+ }
20
+ return Math.abs(hash);
21
+ }
22
+ function getQueryColor(queryName) {
23
+ const hash = hashString(queryName);
24
+ return colors_1.category10[hash % colors_1.category10.length];
25
+ }
26
+ function applyAlpha(color, alpha) {
27
+ if (alpha === 1) {
28
+ return color;
29
+ }
30
+ return (0, colord_1.colord)(color).alpha(alpha).toRgbString();
18
31
  }
19
32
  async function drawDotplot(ctx, props) {
20
33
  var _a, _b;
21
- const { config, views, height, drawCigar, theme } = props;
34
+ const { config, views, height, drawCigar, theme, alpha = 1, minAlignmentLength = 0, colorBy: colorByOverride, } = props;
22
35
  const color = (0, configuration_1.readConfObject)(config, 'color');
23
36
  const posColor = (0, configuration_1.readConfObject)(config, 'posColor');
24
37
  const negColor = (0, configuration_1.readConfObject)(config, 'negColor');
25
- const colorBy = (0, configuration_1.readConfObject)(config, 'colorBy');
38
+ const colorBy = colorByOverride !== null && colorByOverride !== void 0 ? colorByOverride : (0, configuration_1.readConfObject)(config, 'colorBy');
26
39
  const lineWidth = (0, configuration_1.readConfObject)(config, 'lineWidth');
27
40
  const thresholds = (0, configuration_1.readConfObject)(config, 'thresholds');
28
41
  const palette = (0, configuration_1.readConfObject)(config, 'thresholdsPalette');
@@ -35,60 +48,6 @@ async function drawDotplot(ctx, props) {
35
48
  ctx.lineWidth = lineWidth;
36
49
  const { bpPerPx: hBpPerPx } = hview;
37
50
  const { bpPerPx: vBpPerPx } = vview;
38
- function clampWithWarnX(num, min, max, feature) {
39
- const strand = feature.get('strand') || 1;
40
- if (strand === -1) {
41
- ;
42
- [max, min] = [min, max];
43
- }
44
- if (num < min - fudgeFactor) {
45
- let start = feature.get('start');
46
- let end = feature.get('end');
47
- const refName = feature.get('refName');
48
- if (strand === -1) {
49
- ;
50
- [end, start] = [start, end];
51
- }
52
- warnings.push({
53
- message: `feature at (X ${refName}:${start}-${end}) ${r} ${lt}`,
54
- effect: 'clipped the feature',
55
- });
56
- return min;
57
- }
58
- if (num > max + fudgeFactor) {
59
- const strand = feature.get('strand') || 1;
60
- const start = strand === 1 ? feature.get('start') : feature.get('end');
61
- const end = strand === 1 ? feature.get('end') : feature.get('start');
62
- const refName = feature.get('refName');
63
- warnings.push({
64
- message: `feature at (X ${refName}:${start}-${end}) ${r} ${gt}`,
65
- effect: 'clipped the feature',
66
- });
67
- return max;
68
- }
69
- return num;
70
- }
71
- function clampWithWarnY(num, min, max, feature) {
72
- if (num < min - fudgeFactor) {
73
- const mate = feature.get('mate');
74
- const { refName, start, end } = mate;
75
- warnings.push({
76
- message: `feature at (Y ${refName}:${start}-${end}) ${r} ${lt}`,
77
- effect: 'clipped the feature',
78
- });
79
- return min;
80
- }
81
- if (num > max + fudgeFactor) {
82
- const mate = feature.get('mate');
83
- const { refName, start, end } = mate;
84
- warnings.push({
85
- message: `feature at (Y ${refName}:${start}-${end}) ${r} ${gt}`,
86
- effect: 'clipped the feature',
87
- });
88
- return max;
89
- }
90
- return num;
91
- }
92
51
  const hsnap = {
93
52
  ...(0, mobx_state_tree_1.getSnapshot)(hview),
94
53
  staticBlocks: hview.staticBlocks,
@@ -100,41 +59,76 @@ async function drawDotplot(ctx, props) {
100
59
  width: vview.width,
101
60
  };
102
61
  const t = (0, ui_1.createJBrowseTheme)(theme);
103
- for (const feature of hview.features || []) {
62
+ const features = hview.features || [];
63
+ let posColorWithAlpha;
64
+ let negColorWithAlpha;
65
+ let defaultColorWithAlpha;
66
+ const queryColorCache = new Map();
67
+ const getQueryColorWithAlpha = (queryName) => {
68
+ if (!queryColorCache.has(queryName)) {
69
+ const c = getQueryColor(queryName);
70
+ queryColorCache.set(queryName, applyAlpha(c, alpha));
71
+ }
72
+ return queryColorCache.get(queryName);
73
+ };
74
+ if (colorBy === 'strand') {
75
+ posColorWithAlpha = applyAlpha(posColor, alpha);
76
+ negColorWithAlpha = applyAlpha(negColor, alpha);
77
+ }
78
+ else if (colorBy === 'default' && !isCallback) {
79
+ const c = color === '#f0f' ? t.palette.text.primary : color;
80
+ defaultColorWithAlpha = applyAlpha(c, alpha);
81
+ }
82
+ for (const feature of features) {
104
83
  const strand = feature.get('strand') || 1;
105
- const start = strand === 1 ? feature.get('start') : feature.get('end');
106
- const end = strand === 1 ? feature.get('end') : feature.get('start');
84
+ const fStart = feature.get('start');
85
+ const fEnd = feature.get('end');
86
+ if (minAlignmentLength > 0) {
87
+ const alignmentLength = Math.abs(fEnd - fStart);
88
+ if (alignmentLength < minAlignmentLength) {
89
+ continue;
90
+ }
91
+ }
107
92
  const refName = feature.get('refName');
108
93
  const mate = feature.get('mate');
109
94
  const mateRef = mate.refName;
110
- let r = 'black';
111
- if (colorBy === 'identity') {
112
- const identity = feature.get('identity');
113
- for (let i = 0; i < thresholds.length; i++) {
114
- if (identity > +thresholds[i]) {
115
- r = palette[i] || 'black';
116
- break;
117
- }
118
- }
119
- }
120
- else if (colorBy === 'meanQueryIdentity') {
121
- r = `hsl(${feature.get('meanScore') * 200},100%,40%)`;
95
+ const start = strand === 1 ? fStart : fEnd;
96
+ const end = strand === 1 ? fEnd : fStart;
97
+ let colorWithAlpha;
98
+ if (colorBy === 'strand') {
99
+ colorWithAlpha = strand === -1 ? negColorWithAlpha : posColorWithAlpha;
122
100
  }
123
- else if (colorBy === 'mappingQuality') {
124
- r = `hsl(${feature.get('mappingQual')},100%,40%)`;
101
+ else if (colorBy === 'query') {
102
+ const queryName = refName;
103
+ colorWithAlpha = getQueryColorWithAlpha(queryName);
125
104
  }
126
- else if (colorBy === 'strand') {
127
- r = strand === -1 ? negColor : posColor;
105
+ else if (colorBy === 'default' && !isCallback) {
106
+ colorWithAlpha = defaultColorWithAlpha;
128
107
  }
129
- else if (colorBy === 'default') {
130
- r = isCallback
131
- ? (0, configuration_1.readConfObject)(config, 'color', { feature })
132
- : color === '#f0f'
133
- ? t.palette.text.primary
134
- : color;
108
+ else {
109
+ let r = 'black';
110
+ if (colorBy === 'identity') {
111
+ const identity = feature.get('identity');
112
+ for (let i = 0; i < thresholds.length; i++) {
113
+ if (identity > +thresholds[i]) {
114
+ r = palette[i] || 'black';
115
+ break;
116
+ }
117
+ }
118
+ }
119
+ else if (colorBy === 'meanQueryIdentity') {
120
+ r = `hsl(${feature.get('meanScore') * 200},100%,40%)`;
121
+ }
122
+ else if (colorBy === 'mappingQuality') {
123
+ r = `hsl(${feature.get('mappingQual')},100%,40%)`;
124
+ }
125
+ else if (colorBy === 'default' && isCallback) {
126
+ r = (0, configuration_1.readConfObject)(config, 'color', { feature });
127
+ }
128
+ colorWithAlpha = applyAlpha(r, alpha);
135
129
  }
136
- ctx.fillStyle = r;
137
- ctx.strokeStyle = r;
130
+ ctx.fillStyle = colorWithAlpha;
131
+ ctx.strokeStyle = colorWithAlpha;
138
132
  const b10 = (0, Base1DUtils_1.bpToPx)({ self: hsnap, refName, coord: start });
139
133
  const b20 = (0, Base1DUtils_1.bpToPx)({ self: hsnap, refName, coord: end });
140
134
  const e10 = (0, Base1DUtils_1.bpToPx)({ self: vsnap, refName: mateRef, coord: mate.start });
@@ -148,7 +142,9 @@ async function drawDotplot(ctx, props) {
148
142
  const e1 = e10.offsetPx - db2;
149
143
  const e2 = e20.offsetPx - db2;
150
144
  if (Math.abs(b1 - b2) <= 4 && Math.abs(e1 - e2) <= 4) {
151
- drawCir(ctx, b1, height - e1, lineWidth);
145
+ ctx.beginPath();
146
+ ctx.arc(b1, height - e1, lineWidth / 2, 0, 2 * Math.PI);
147
+ ctx.fill();
152
148
  }
153
149
  else {
154
150
  let currX = b1;
@@ -173,8 +169,8 @@ async function drawDotplot(ctx, props) {
173
169
  else if (op === 'I') {
174
170
  currY += val / vBpPerPx;
175
171
  }
176
- currX = clampWithWarnX(currX, b1, b2, feature);
177
- currY = clampWithWarnY(currY, e1, e2, feature);
172
+ currX = (0, clamp_1.clampWithWarnX)(currX, b1, b2, feature, warnings);
173
+ currY = (0, clamp_1.clampWithWarnY)(currY, e1, e2, feature, warnings);
178
174
  if (Math.abs(currX - lastDrawnX) > 0.5 ||
179
175
  Math.abs(currY - lastDrawnY) > 0.5) {
180
176
  ctx.lineTo(currX, height - currY);
@@ -196,13 +192,13 @@ async function drawDotplot(ctx, props) {
196
192
  if (warnings.length <= 5) {
197
193
  if (b10 === undefined || b20 === undefined) {
198
194
  warnings.push({
199
- message: `feature at (X ${refName}:${start}-${end}) not plotted, fell outside of range`,
195
+ message: `feature at (X-coord: ${refName}:${start}-${end}) not plotted, fell outside of range`,
200
196
  effect: 'feature not rendered',
201
197
  });
202
198
  }
203
199
  else {
204
200
  warnings.push({
205
- message: `feature at (Y ${mateRef}:${mate.start}-${mate.end}) not plotted, fell outside of range`,
201
+ message: `feature at (Y-coord: ${mateRef}:${mate.start}-${mate.end}) not plotted, fell outside of range`,
206
202
  effect: 'feature not rendered',
207
203
  });
208
204
  }
@@ -20,16 +20,18 @@ const Dotplot1DView = Base1DViewModel_1.default.extend(self => {
20
20
  return scaleFactor.get();
21
21
  },
22
22
  get maxBpPerPx() {
23
- return self.totalBp / (self.width - 50);
23
+ return self.totalBp / (self.width * 0.9);
24
24
  },
25
25
  get minBpPerPx() {
26
26
  return 1 / 50;
27
27
  },
28
28
  get maxOffset() {
29
- return self.displayedRegionsTotalPx - self.width * 0.2;
29
+ const leftPadding = 10;
30
+ return self.displayedRegionsTotalPx - leftPadding;
30
31
  },
31
32
  get minOffset() {
32
- return -self.width * 0.8;
33
+ const rightPadding = 30;
34
+ return -self.width + rightPadding;
33
35
  },
34
36
  },
35
37
  actions: {