@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.
- 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,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;
|
|
@@ -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
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
const
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
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
|
-
|
|
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
|
|
106
|
-
const
|
|
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
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
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 === '
|
|
124
|
-
|
|
101
|
+
else if (colorBy === 'query') {
|
|
102
|
+
const queryName = refName;
|
|
103
|
+
colorWithAlpha = getQueryColorWithAlpha(queryName);
|
|
125
104
|
}
|
|
126
|
-
else if (colorBy === '
|
|
127
|
-
|
|
105
|
+
else if (colorBy === 'default' && !isCallback) {
|
|
106
|
+
colorWithAlpha = defaultColorWithAlpha;
|
|
128
107
|
}
|
|
129
|
-
else
|
|
130
|
-
r =
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
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 =
|
|
137
|
-
ctx.strokeStyle =
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
29
|
+
const leftPadding = 10;
|
|
30
|
+
return self.displayedRegionsTotalPx - leftPadding;
|
|
30
31
|
},
|
|
31
32
|
get minOffset() {
|
|
32
|
-
|
|
33
|
+
const rightPadding = 30;
|
|
34
|
+
return -self.width + rightPadding;
|
|
33
35
|
},
|
|
34
36
|
},
|
|
35
37
|
actions: {
|