@jbrowse/plugin-dotplot-view 2.14.0 → 2.15.1
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/DotplotDisplay/stateModelFactory.d.ts +6 -1
- package/dist/DotplotRenderer/DotplotRenderer.d.ts +1 -9
- package/dist/DotplotRenderer/DotplotRenderer.js +25 -214
- package/dist/DotplotRenderer/drawDotplot.d.ts +21 -0
- package/dist/DotplotRenderer/drawDotplot.js +216 -0
- package/dist/DotplotView/components/{DotplotTooltip.d.ts → DotplotTooltipClick.d.ts} +2 -8
- package/dist/DotplotView/components/DotplotTooltipClick.js +21 -0
- package/dist/DotplotView/components/DotplotTooltipMouseover.d.ts +10 -0
- package/dist/DotplotView/components/DotplotTooltipMouseover.js +20 -0
- package/dist/DotplotView/components/DotplotView.js +6 -3
- package/dist/DotplotView/model.d.ts +1 -0
- package/esm/DotplotDisplay/stateModelFactory.d.ts +6 -1
- package/esm/DotplotRenderer/DotplotRenderer.d.ts +1 -9
- package/esm/DotplotRenderer/DotplotRenderer.js +2 -214
- package/esm/DotplotRenderer/drawDotplot.d.ts +21 -0
- package/esm/DotplotRenderer/drawDotplot.js +213 -0
- package/esm/DotplotView/components/{DotplotTooltip.d.ts → DotplotTooltipClick.d.ts} +2 -8
- package/esm/DotplotView/components/DotplotTooltipClick.js +15 -0
- package/esm/DotplotView/components/DotplotTooltipMouseover.d.ts +10 -0
- package/esm/DotplotView/components/DotplotTooltipMouseover.js +15 -0
- package/esm/DotplotView/components/DotplotView.js +7 -4
- package/esm/DotplotView/model.d.ts +1 -0
- package/package.json +4 -4
- package/dist/DotplotView/components/DotplotTooltip.js +0 -84
- package/esm/DotplotView/components/DotplotTooltip.js +0 -78
|
@@ -27,14 +27,6 @@ interface DotplotRenderArgs extends RenderArgs {
|
|
|
27
27
|
export default class DotplotRenderer extends ComparativeRenderer {
|
|
28
28
|
supportsSVG: boolean;
|
|
29
29
|
renameRegionsIfNeeded(args: DotplotRenderArgs): Promise<DotplotRenderArgs>;
|
|
30
|
-
drawDotplot(ctx: CanvasRenderingContext2D, props: DotplotRenderArgsDeserialized & {
|
|
31
|
-
views: Dotplot1DViewModel[];
|
|
32
|
-
}): Promise<{
|
|
33
|
-
warnings: {
|
|
34
|
-
message: string;
|
|
35
|
-
effect: string;
|
|
36
|
-
}[];
|
|
37
|
-
}>;
|
|
38
30
|
render(renderProps: DotplotRenderArgsDeserialized): Promise<{
|
|
39
31
|
height: number;
|
|
40
32
|
width: number;
|
|
@@ -52,7 +44,7 @@ export default class DotplotRenderer extends ComparativeRenderer {
|
|
|
52
44
|
offsetY: number;
|
|
53
45
|
bpPerPxX: number;
|
|
54
46
|
bpPerPxY: number;
|
|
55
|
-
reactElement:
|
|
47
|
+
reactElement: React.JSX.Element;
|
|
56
48
|
html?: string;
|
|
57
49
|
} | {
|
|
58
50
|
height: number;
|
|
@@ -1,22 +1,7 @@
|
|
|
1
|
-
import { readConfObject, } from '@jbrowse/core/configuration';
|
|
2
1
|
import { renameRegionsIfNeeded, renderToAbstractCanvas, } from '@jbrowse/core/util';
|
|
3
|
-
import { bpToPx } from '@jbrowse/core/util/Base1DUtils';
|
|
4
|
-
import { getSnapshot } from 'mobx-state-tree';
|
|
5
2
|
import ComparativeRenderer from '@jbrowse/core/pluggableElementTypes/renderers/ComparativeServerSideRendererType';
|
|
6
|
-
import { MismatchParser } from '@jbrowse/plugin-alignments';
|
|
7
3
|
// locals
|
|
8
4
|
import { Dotplot1DView } from '../DotplotView/model';
|
|
9
|
-
import { createJBrowseTheme } from '@jbrowse/core/ui';
|
|
10
|
-
const { parseCigar } = MismatchParser;
|
|
11
|
-
const r = 'fell outside of range due to CIGAR string';
|
|
12
|
-
const lt = '(less than min coordinate of feature)';
|
|
13
|
-
const gt = '(greater than max coordinate of feature)';
|
|
14
|
-
const fudgeFactor = 1; // allow 1px fuzzyness before warn
|
|
15
|
-
function drawCir(ctx, x, y, r = 1) {
|
|
16
|
-
ctx.beginPath();
|
|
17
|
-
ctx.arc(x, y, r / 2, 0, 2 * Math.PI);
|
|
18
|
-
ctx.fill();
|
|
19
|
-
}
|
|
20
5
|
export default class DotplotRenderer extends ComparativeRenderer {
|
|
21
6
|
constructor() {
|
|
22
7
|
super(...arguments);
|
|
@@ -42,204 +27,6 @@ export default class DotplotRenderer extends ComparativeRenderer {
|
|
|
42
27
|
view.vview.displayedRegions = await process(view.vview.displayedRegions);
|
|
43
28
|
return args;
|
|
44
29
|
}
|
|
45
|
-
async drawDotplot(ctx, props) {
|
|
46
|
-
var _a, _b;
|
|
47
|
-
const { config, views, height, drawCigar, theme } = props;
|
|
48
|
-
const color = readConfObject(config, 'color');
|
|
49
|
-
const posColor = readConfObject(config, 'posColor');
|
|
50
|
-
const negColor = readConfObject(config, 'negColor');
|
|
51
|
-
const colorBy = readConfObject(config, 'colorBy');
|
|
52
|
-
const lineWidth = readConfObject(config, 'lineWidth');
|
|
53
|
-
const thresholds = readConfObject(config, 'thresholds');
|
|
54
|
-
const palette = readConfObject(config, 'thresholdsPalette');
|
|
55
|
-
const isCallback = config.color.isCallback;
|
|
56
|
-
const hview = views[0];
|
|
57
|
-
const vview = views[1];
|
|
58
|
-
const db1 = (_a = hview.dynamicBlocks.contentBlocks[0]) === null || _a === void 0 ? void 0 : _a.offsetPx;
|
|
59
|
-
const db2 = (_b = vview.dynamicBlocks.contentBlocks[0]) === null || _b === void 0 ? void 0 : _b.offsetPx;
|
|
60
|
-
const warnings = [];
|
|
61
|
-
ctx.lineWidth = lineWidth;
|
|
62
|
-
// we operate on snapshots of these attributes of the hview/vview because
|
|
63
|
-
// it is significantly faster than accessing the mobx objects
|
|
64
|
-
const { bpPerPx: hBpPerPx } = hview;
|
|
65
|
-
const { bpPerPx: vBpPerPx } = vview;
|
|
66
|
-
function clampWithWarnX(num, min, max, feature) {
|
|
67
|
-
const strand = feature.get('strand') || 1;
|
|
68
|
-
if (strand === -1) {
|
|
69
|
-
;
|
|
70
|
-
[max, min] = [min, max];
|
|
71
|
-
}
|
|
72
|
-
if (num < min - fudgeFactor) {
|
|
73
|
-
let start = feature.get('start');
|
|
74
|
-
let end = feature.get('end');
|
|
75
|
-
const refName = feature.get('refName');
|
|
76
|
-
if (strand === -1) {
|
|
77
|
-
;
|
|
78
|
-
[end, start] = [start, end];
|
|
79
|
-
}
|
|
80
|
-
warnings.push({
|
|
81
|
-
message: `feature at (X ${refName}:${start}-${end}) ${r} ${lt}`,
|
|
82
|
-
effect: 'clipped the feature',
|
|
83
|
-
});
|
|
84
|
-
return min;
|
|
85
|
-
}
|
|
86
|
-
if (num > max + fudgeFactor) {
|
|
87
|
-
const strand = feature.get('strand') || 1;
|
|
88
|
-
const start = strand === 1 ? feature.get('start') : feature.get('end');
|
|
89
|
-
const end = strand === 1 ? feature.get('end') : feature.get('start');
|
|
90
|
-
const refName = feature.get('refName');
|
|
91
|
-
warnings.push({
|
|
92
|
-
message: `feature at (X ${refName}:${start}-${end}) ${r} ${gt}`,
|
|
93
|
-
effect: 'clipped the feature',
|
|
94
|
-
});
|
|
95
|
-
return max;
|
|
96
|
-
}
|
|
97
|
-
return num;
|
|
98
|
-
}
|
|
99
|
-
function clampWithWarnY(num, min, max, feature) {
|
|
100
|
-
if (num < min - fudgeFactor) {
|
|
101
|
-
const mate = feature.get('mate');
|
|
102
|
-
const { refName, start, end } = mate;
|
|
103
|
-
warnings.push({
|
|
104
|
-
message: `feature at (Y ${refName}:${start}-${end}) ${r} ${lt}`,
|
|
105
|
-
effect: 'clipped the feature',
|
|
106
|
-
});
|
|
107
|
-
return min;
|
|
108
|
-
}
|
|
109
|
-
if (num > max + fudgeFactor) {
|
|
110
|
-
const mate = feature.get('mate');
|
|
111
|
-
const { refName, start, end } = mate;
|
|
112
|
-
warnings.push({
|
|
113
|
-
message: `feature at (Y ${refName}:${start}-${end}) ${r} ${gt}`,
|
|
114
|
-
effect: 'clipped the feature',
|
|
115
|
-
});
|
|
116
|
-
return max;
|
|
117
|
-
}
|
|
118
|
-
return num;
|
|
119
|
-
}
|
|
120
|
-
const hsnap = {
|
|
121
|
-
...getSnapshot(hview),
|
|
122
|
-
staticBlocks: hview.staticBlocks,
|
|
123
|
-
width: hview.width,
|
|
124
|
-
};
|
|
125
|
-
const vsnap = {
|
|
126
|
-
...getSnapshot(vview),
|
|
127
|
-
staticBlocks: vview.staticBlocks,
|
|
128
|
-
width: vview.width,
|
|
129
|
-
};
|
|
130
|
-
const t = createJBrowseTheme(theme);
|
|
131
|
-
for (const feature of hview.features || []) {
|
|
132
|
-
const strand = feature.get('strand') || 1;
|
|
133
|
-
const start = strand === 1 ? feature.get('start') : feature.get('end');
|
|
134
|
-
const end = strand === 1 ? feature.get('end') : feature.get('start');
|
|
135
|
-
const refName = feature.get('refName');
|
|
136
|
-
const mate = feature.get('mate');
|
|
137
|
-
const mateRef = mate.refName;
|
|
138
|
-
let r = 'black';
|
|
139
|
-
if (colorBy === 'identity') {
|
|
140
|
-
const identity = feature.get('identity');
|
|
141
|
-
for (let i = 0; i < thresholds.length; i++) {
|
|
142
|
-
if (identity > +thresholds[i]) {
|
|
143
|
-
r = palette[i] || 'black';
|
|
144
|
-
break;
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
else if (colorBy === 'meanQueryIdentity') {
|
|
149
|
-
r = `hsl(${feature.get('meanScore') * 200},100%,40%)`;
|
|
150
|
-
}
|
|
151
|
-
else if (colorBy === 'mappingQuality') {
|
|
152
|
-
r = `hsl(${feature.get('mappingQual')},100%,40%)`;
|
|
153
|
-
}
|
|
154
|
-
else if (colorBy === 'strand') {
|
|
155
|
-
r = strand === -1 ? negColor : posColor;
|
|
156
|
-
}
|
|
157
|
-
else if (colorBy === 'default') {
|
|
158
|
-
r = isCallback
|
|
159
|
-
? readConfObject(config, 'color', { feature })
|
|
160
|
-
: color === '#f0f'
|
|
161
|
-
? t.palette.text.primary
|
|
162
|
-
: color;
|
|
163
|
-
}
|
|
164
|
-
ctx.fillStyle = r;
|
|
165
|
-
ctx.strokeStyle = r;
|
|
166
|
-
const b10 = bpToPx({ self: hsnap, refName, coord: start });
|
|
167
|
-
const b20 = bpToPx({ self: hsnap, refName, coord: end });
|
|
168
|
-
const e10 = bpToPx({ self: vsnap, refName: mateRef, coord: mate.start });
|
|
169
|
-
const e20 = bpToPx({ self: vsnap, refName: mateRef, coord: mate.end });
|
|
170
|
-
if (b10 !== undefined &&
|
|
171
|
-
b20 !== undefined &&
|
|
172
|
-
e10 !== undefined &&
|
|
173
|
-
e20 !== undefined) {
|
|
174
|
-
const b1 = b10.offsetPx - db1;
|
|
175
|
-
const b2 = b20.offsetPx - db1;
|
|
176
|
-
const e1 = e10.offsetPx - db2;
|
|
177
|
-
const e2 = e20.offsetPx - db2;
|
|
178
|
-
if (Math.abs(b1 - b2) <= 4 && Math.abs(e1 - e2) <= 4) {
|
|
179
|
-
drawCir(ctx, b1, height - e1, lineWidth);
|
|
180
|
-
}
|
|
181
|
-
else {
|
|
182
|
-
let currX = b1;
|
|
183
|
-
let currY = e1;
|
|
184
|
-
const cigar = feature.get('CIGAR');
|
|
185
|
-
if (drawCigar && cigar) {
|
|
186
|
-
const cigarOps = parseCigar(cigar);
|
|
187
|
-
ctx.beginPath();
|
|
188
|
-
ctx.moveTo(currX, height - currY);
|
|
189
|
-
let lastDrawnX = currX;
|
|
190
|
-
let lastDrawnY = currX;
|
|
191
|
-
for (let i = 0; i < cigarOps.length; i += 2) {
|
|
192
|
-
const val = +cigarOps[i];
|
|
193
|
-
const op = cigarOps[i + 1];
|
|
194
|
-
if (op === 'M' || op === '=' || op === 'X') {
|
|
195
|
-
currX += (val / hBpPerPx) * strand;
|
|
196
|
-
currY += val / vBpPerPx;
|
|
197
|
-
}
|
|
198
|
-
else if (op === 'D' || op === 'N') {
|
|
199
|
-
currX += (val / hBpPerPx) * strand;
|
|
200
|
-
}
|
|
201
|
-
else if (op === 'I') {
|
|
202
|
-
currY += val / vBpPerPx;
|
|
203
|
-
}
|
|
204
|
-
currX = clampWithWarnX(currX, b1, b2, feature);
|
|
205
|
-
currY = clampWithWarnY(currY, e1, e2, feature);
|
|
206
|
-
// only draw a line segment if it is bigger than 0.5px
|
|
207
|
-
if (Math.abs(currX - lastDrawnX) > 0.5 ||
|
|
208
|
-
Math.abs(currY - lastDrawnY) > 0.5) {
|
|
209
|
-
ctx.lineTo(currX, height - currY);
|
|
210
|
-
lastDrawnX = currX;
|
|
211
|
-
lastDrawnY = currY;
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
ctx.stroke();
|
|
215
|
-
}
|
|
216
|
-
else {
|
|
217
|
-
ctx.beginPath();
|
|
218
|
-
ctx.moveTo(b1, height - e1);
|
|
219
|
-
ctx.lineTo(b2, height - e2);
|
|
220
|
-
ctx.stroke();
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
else {
|
|
225
|
-
if (warnings.length <= 5) {
|
|
226
|
-
if (b10 === undefined || b20 === undefined) {
|
|
227
|
-
warnings.push({
|
|
228
|
-
message: `feature at (X ${refName}:${start}-${end}) not plotted, fell outside of range`,
|
|
229
|
-
effect: 'feature not rendered',
|
|
230
|
-
});
|
|
231
|
-
}
|
|
232
|
-
else {
|
|
233
|
-
warnings.push({
|
|
234
|
-
message: `feature at (Y ${mateRef}:${mate.start}-${mate.end}) not plotted, fell outside of range`,
|
|
235
|
-
effect: 'feature not rendered',
|
|
236
|
-
});
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
return { warnings };
|
|
242
|
-
}
|
|
243
30
|
async render(renderProps) {
|
|
244
31
|
var _a, _b;
|
|
245
32
|
const { width, height, view: { hview, vview }, } = renderProps;
|
|
@@ -255,7 +42,8 @@ export default class DotplotRenderer extends ComparativeRenderer {
|
|
|
255
42
|
regions: target.dynamicBlocks.contentBlocks,
|
|
256
43
|
});
|
|
257
44
|
target.setFeatures(feats);
|
|
258
|
-
const
|
|
45
|
+
const { drawDotplot } = await import('./drawDotplot');
|
|
46
|
+
const ret = await renderToAbstractCanvas(width, height, renderProps, ctx => drawDotplot(ctx, { ...renderProps, views }));
|
|
259
47
|
const results = await super.render({
|
|
260
48
|
...renderProps,
|
|
261
49
|
...ret,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { AnyConfigurationModel } from '@jbrowse/core/configuration';
|
|
2
|
+
import { RenderArgsDeserialized } from '@jbrowse/core/pluggableElementTypes/renderers/ComparativeServerSideRendererType';
|
|
3
|
+
import { Dotplot1DViewModel } from '../DotplotView/model';
|
|
4
|
+
export interface DotplotRenderArgsDeserialized extends RenderArgsDeserialized {
|
|
5
|
+
adapterConfig: AnyConfigurationModel;
|
|
6
|
+
height: number;
|
|
7
|
+
width: number;
|
|
8
|
+
highResolutionScaling: number;
|
|
9
|
+
view: {
|
|
10
|
+
hview: Dotplot1DViewModel;
|
|
11
|
+
vview: Dotplot1DViewModel;
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
export declare function drawDotplot(ctx: CanvasRenderingContext2D, props: DotplotRenderArgsDeserialized & {
|
|
15
|
+
views: Dotplot1DViewModel[];
|
|
16
|
+
}): Promise<{
|
|
17
|
+
warnings: {
|
|
18
|
+
message: string;
|
|
19
|
+
effect: string;
|
|
20
|
+
}[];
|
|
21
|
+
}>;
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
import { readConfObject, } from '@jbrowse/core/configuration';
|
|
2
|
+
import { bpToPx } from '@jbrowse/core/util/Base1DUtils';
|
|
3
|
+
import { getSnapshot } from 'mobx-state-tree';
|
|
4
|
+
import { MismatchParser } from '@jbrowse/plugin-alignments';
|
|
5
|
+
import { createJBrowseTheme } from '@jbrowse/core/ui';
|
|
6
|
+
const { parseCigar } = MismatchParser;
|
|
7
|
+
const r = 'fell outside of range due to CIGAR string';
|
|
8
|
+
const lt = '(less than min coordinate of feature)';
|
|
9
|
+
const gt = '(greater than max coordinate of feature)';
|
|
10
|
+
const fudgeFactor = 1; // allow 1px fuzzyness before warn
|
|
11
|
+
function drawCir(ctx, x, y, r = 1) {
|
|
12
|
+
ctx.beginPath();
|
|
13
|
+
ctx.arc(x, y, r / 2, 0, 2 * Math.PI);
|
|
14
|
+
ctx.fill();
|
|
15
|
+
}
|
|
16
|
+
export async function drawDotplot(ctx, props) {
|
|
17
|
+
var _a, _b;
|
|
18
|
+
const { config, views, height, drawCigar, theme } = props;
|
|
19
|
+
const color = readConfObject(config, 'color');
|
|
20
|
+
const posColor = readConfObject(config, 'posColor');
|
|
21
|
+
const negColor = readConfObject(config, 'negColor');
|
|
22
|
+
const colorBy = readConfObject(config, 'colorBy');
|
|
23
|
+
const lineWidth = readConfObject(config, 'lineWidth');
|
|
24
|
+
const thresholds = readConfObject(config, 'thresholds');
|
|
25
|
+
const palette = readConfObject(config, 'thresholdsPalette');
|
|
26
|
+
const isCallback = config.color.isCallback;
|
|
27
|
+
const hview = views[0];
|
|
28
|
+
const vview = views[1];
|
|
29
|
+
const db1 = (_a = hview.dynamicBlocks.contentBlocks[0]) === null || _a === void 0 ? void 0 : _a.offsetPx;
|
|
30
|
+
const db2 = (_b = vview.dynamicBlocks.contentBlocks[0]) === null || _b === void 0 ? void 0 : _b.offsetPx;
|
|
31
|
+
const warnings = [];
|
|
32
|
+
ctx.lineWidth = lineWidth;
|
|
33
|
+
// we operate on snapshots of these attributes of the hview/vview because
|
|
34
|
+
// it is significantly faster than accessing the mobx objects
|
|
35
|
+
const { bpPerPx: hBpPerPx } = hview;
|
|
36
|
+
const { bpPerPx: vBpPerPx } = vview;
|
|
37
|
+
function clampWithWarnX(num, min, max, feature) {
|
|
38
|
+
const strand = feature.get('strand') || 1;
|
|
39
|
+
if (strand === -1) {
|
|
40
|
+
;
|
|
41
|
+
[max, min] = [min, max];
|
|
42
|
+
}
|
|
43
|
+
if (num < min - fudgeFactor) {
|
|
44
|
+
let start = feature.get('start');
|
|
45
|
+
let end = feature.get('end');
|
|
46
|
+
const refName = feature.get('refName');
|
|
47
|
+
if (strand === -1) {
|
|
48
|
+
;
|
|
49
|
+
[end, start] = [start, end];
|
|
50
|
+
}
|
|
51
|
+
warnings.push({
|
|
52
|
+
message: `feature at (X ${refName}:${start}-${end}) ${r} ${lt}`,
|
|
53
|
+
effect: 'clipped the feature',
|
|
54
|
+
});
|
|
55
|
+
return min;
|
|
56
|
+
}
|
|
57
|
+
if (num > max + fudgeFactor) {
|
|
58
|
+
const strand = feature.get('strand') || 1;
|
|
59
|
+
const start = strand === 1 ? feature.get('start') : feature.get('end');
|
|
60
|
+
const end = strand === 1 ? feature.get('end') : feature.get('start');
|
|
61
|
+
const refName = feature.get('refName');
|
|
62
|
+
warnings.push({
|
|
63
|
+
message: `feature at (X ${refName}:${start}-${end}) ${r} ${gt}`,
|
|
64
|
+
effect: 'clipped the feature',
|
|
65
|
+
});
|
|
66
|
+
return max;
|
|
67
|
+
}
|
|
68
|
+
return num;
|
|
69
|
+
}
|
|
70
|
+
function clampWithWarnY(num, min, max, feature) {
|
|
71
|
+
if (num < min - fudgeFactor) {
|
|
72
|
+
const mate = feature.get('mate');
|
|
73
|
+
const { refName, start, end } = mate;
|
|
74
|
+
warnings.push({
|
|
75
|
+
message: `feature at (Y ${refName}:${start}-${end}) ${r} ${lt}`,
|
|
76
|
+
effect: 'clipped the feature',
|
|
77
|
+
});
|
|
78
|
+
return min;
|
|
79
|
+
}
|
|
80
|
+
if (num > max + fudgeFactor) {
|
|
81
|
+
const mate = feature.get('mate');
|
|
82
|
+
const { refName, start, end } = mate;
|
|
83
|
+
warnings.push({
|
|
84
|
+
message: `feature at (Y ${refName}:${start}-${end}) ${r} ${gt}`,
|
|
85
|
+
effect: 'clipped the feature',
|
|
86
|
+
});
|
|
87
|
+
return max;
|
|
88
|
+
}
|
|
89
|
+
return num;
|
|
90
|
+
}
|
|
91
|
+
const hsnap = {
|
|
92
|
+
...getSnapshot(hview),
|
|
93
|
+
staticBlocks: hview.staticBlocks,
|
|
94
|
+
width: hview.width,
|
|
95
|
+
};
|
|
96
|
+
const vsnap = {
|
|
97
|
+
...getSnapshot(vview),
|
|
98
|
+
staticBlocks: vview.staticBlocks,
|
|
99
|
+
width: vview.width,
|
|
100
|
+
};
|
|
101
|
+
const t = createJBrowseTheme(theme);
|
|
102
|
+
for (const feature of hview.features || []) {
|
|
103
|
+
const strand = feature.get('strand') || 1;
|
|
104
|
+
const start = strand === 1 ? feature.get('start') : feature.get('end');
|
|
105
|
+
const end = strand === 1 ? feature.get('end') : feature.get('start');
|
|
106
|
+
const refName = feature.get('refName');
|
|
107
|
+
const mate = feature.get('mate');
|
|
108
|
+
const mateRef = mate.refName;
|
|
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 === 'strand') {
|
|
126
|
+
r = strand === -1 ? negColor : posColor;
|
|
127
|
+
}
|
|
128
|
+
else if (colorBy === 'default') {
|
|
129
|
+
r = isCallback
|
|
130
|
+
? readConfObject(config, 'color', { feature })
|
|
131
|
+
: color === '#f0f'
|
|
132
|
+
? t.palette.text.primary
|
|
133
|
+
: color;
|
|
134
|
+
}
|
|
135
|
+
ctx.fillStyle = r;
|
|
136
|
+
ctx.strokeStyle = r;
|
|
137
|
+
const b10 = bpToPx({ self: hsnap, refName, coord: start });
|
|
138
|
+
const b20 = bpToPx({ self: hsnap, refName, coord: end });
|
|
139
|
+
const e10 = bpToPx({ self: vsnap, refName: mateRef, coord: mate.start });
|
|
140
|
+
const e20 = bpToPx({ self: vsnap, refName: mateRef, coord: mate.end });
|
|
141
|
+
if (b10 !== undefined &&
|
|
142
|
+
b20 !== undefined &&
|
|
143
|
+
e10 !== undefined &&
|
|
144
|
+
e20 !== undefined) {
|
|
145
|
+
const b1 = b10.offsetPx - db1;
|
|
146
|
+
const b2 = b20.offsetPx - db1;
|
|
147
|
+
const e1 = e10.offsetPx - db2;
|
|
148
|
+
const e2 = e20.offsetPx - db2;
|
|
149
|
+
if (Math.abs(b1 - b2) <= 4 && Math.abs(e1 - e2) <= 4) {
|
|
150
|
+
drawCir(ctx, b1, height - e1, lineWidth);
|
|
151
|
+
}
|
|
152
|
+
else {
|
|
153
|
+
let currX = b1;
|
|
154
|
+
let currY = e1;
|
|
155
|
+
const cigar = feature.get('CIGAR');
|
|
156
|
+
if (drawCigar && cigar) {
|
|
157
|
+
const cigarOps = parseCigar(cigar);
|
|
158
|
+
ctx.beginPath();
|
|
159
|
+
ctx.moveTo(currX, height - currY);
|
|
160
|
+
let lastDrawnX = currX;
|
|
161
|
+
let lastDrawnY = currX;
|
|
162
|
+
for (let i = 0; i < cigarOps.length; i += 2) {
|
|
163
|
+
const val = +cigarOps[i];
|
|
164
|
+
const op = cigarOps[i + 1];
|
|
165
|
+
if (op === 'M' || op === '=' || op === 'X') {
|
|
166
|
+
currX += (val / hBpPerPx) * strand;
|
|
167
|
+
currY += val / vBpPerPx;
|
|
168
|
+
}
|
|
169
|
+
else if (op === 'D' || op === 'N') {
|
|
170
|
+
currX += (val / hBpPerPx) * strand;
|
|
171
|
+
}
|
|
172
|
+
else if (op === 'I') {
|
|
173
|
+
currY += val / vBpPerPx;
|
|
174
|
+
}
|
|
175
|
+
currX = clampWithWarnX(currX, b1, b2, feature);
|
|
176
|
+
currY = clampWithWarnY(currY, e1, e2, feature);
|
|
177
|
+
// only draw a line segment if it is bigger than 0.5px
|
|
178
|
+
if (Math.abs(currX - lastDrawnX) > 0.5 ||
|
|
179
|
+
Math.abs(currY - lastDrawnY) > 0.5) {
|
|
180
|
+
ctx.lineTo(currX, height - currY);
|
|
181
|
+
lastDrawnX = currX;
|
|
182
|
+
lastDrawnY = currY;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
ctx.stroke();
|
|
186
|
+
}
|
|
187
|
+
else {
|
|
188
|
+
ctx.beginPath();
|
|
189
|
+
ctx.moveTo(b1, height - e1);
|
|
190
|
+
ctx.lineTo(b2, height - e2);
|
|
191
|
+
ctx.stroke();
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
else {
|
|
196
|
+
if (warnings.length <= 5) {
|
|
197
|
+
if (b10 === undefined || b20 === undefined) {
|
|
198
|
+
warnings.push({
|
|
199
|
+
message: `feature at (X ${refName}:${start}-${end}) not plotted, fell outside of range`,
|
|
200
|
+
effect: 'feature not rendered',
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
else {
|
|
204
|
+
warnings.push({
|
|
205
|
+
message: `feature at (Y ${mateRef}:${mate.start}-${mate.end}) not plotted, fell outside of range`,
|
|
206
|
+
effect: 'feature not rendered',
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
return { warnings };
|
|
213
|
+
}
|
|
@@ -1,17 +1,11 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { DotplotViewModel } from '../model';
|
|
3
3
|
type Coord = [number, number] | undefined;
|
|
4
|
-
export declare const
|
|
5
|
-
model: DotplotViewModel;
|
|
6
|
-
mouserect: Coord;
|
|
7
|
-
mouserectClient: Coord;
|
|
8
|
-
xdistance: number;
|
|
9
|
-
}) => React.JSX.Element | null;
|
|
10
|
-
export declare const TooltipWhereClicked: ({ model, mousedown, mousedownClient, xdistance, ydistance, }: {
|
|
4
|
+
export declare const DotplotTooltipClick: ({ model, mousedown, mousedownClient, xdistance, ydistance, }: {
|
|
11
5
|
model: DotplotViewModel;
|
|
12
6
|
mousedown: Coord;
|
|
13
7
|
mousedownClient: Coord;
|
|
14
8
|
xdistance: number;
|
|
15
9
|
ydistance: number;
|
|
16
10
|
}) => React.JSX.Element | null;
|
|
17
|
-
export
|
|
11
|
+
export default DotplotTooltipClick;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { observer } from 'mobx-react';
|
|
3
|
+
import BaseTooltip from '@jbrowse/core/ui/BaseTooltip';
|
|
4
|
+
import { locstr } from './util';
|
|
5
|
+
export const DotplotTooltipClick = observer(function ({ model, mousedown, mousedownClient, xdistance, ydistance, }) {
|
|
6
|
+
const { hview, vview, viewHeight } = model;
|
|
7
|
+
const x = ((mousedownClient === null || mousedownClient === void 0 ? void 0 : mousedownClient[0]) || 0) - (xdistance < 0 ? 0 : 0);
|
|
8
|
+
const y = ((mousedownClient === null || mousedownClient === void 0 ? void 0 : mousedownClient[1]) || 0) - (ydistance < 0 ? 0 : 0);
|
|
9
|
+
return mousedown && Math.abs(xdistance) > 3 && Math.abs(ydistance) > 3 ? (React.createElement(BaseTooltip, { clientPoint: { x, y } },
|
|
10
|
+
`x - ${locstr(mousedown[0], hview)}`,
|
|
11
|
+
React.createElement("br", null),
|
|
12
|
+
`y - ${locstr(viewHeight - mousedown[1], vview)}`,
|
|
13
|
+
React.createElement("br", null))) : null;
|
|
14
|
+
});
|
|
15
|
+
export default DotplotTooltipClick;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { DotplotViewModel } from '../model';
|
|
3
|
+
type Coord = [number, number] | undefined;
|
|
4
|
+
declare const DotplotTooltipMouseover: ({ model, mouserect, mouserectClient, xdistance, }: {
|
|
5
|
+
model: DotplotViewModel;
|
|
6
|
+
mouserect: Coord;
|
|
7
|
+
mouserectClient: Coord;
|
|
8
|
+
xdistance: number;
|
|
9
|
+
}) => React.JSX.Element | null;
|
|
10
|
+
export default DotplotTooltipMouseover;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { observer } from 'mobx-react';
|
|
3
|
+
import BaseTooltip from '@jbrowse/core/ui/BaseTooltip';
|
|
4
|
+
import { locstr } from './util';
|
|
5
|
+
const DotplotTooltipMouseover = observer(function ({ model, mouserect, mouserectClient, xdistance, }) {
|
|
6
|
+
const { hview, vview, viewHeight } = model;
|
|
7
|
+
return mouserect ? (React.createElement(BaseTooltip, { placement: xdistance < 0 ? 'left' : 'right', clientPoint: mouserectClient
|
|
8
|
+
? { x: mouserectClient[0], y: mouserectClient[1] }
|
|
9
|
+
: undefined },
|
|
10
|
+
`x - ${locstr(mouserect[0], hview)}`,
|
|
11
|
+
React.createElement("br", null),
|
|
12
|
+
`y - ${locstr(viewHeight - mouserect[1], vview)}`,
|
|
13
|
+
React.createElement("br", null))) : null;
|
|
14
|
+
});
|
|
15
|
+
export default DotplotTooltipMouseover;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, { useState, useEffect, useRef } from 'react';
|
|
1
|
+
import React, { useState, useEffect, useRef, lazy, Suspense } from 'react';
|
|
2
2
|
import { LoadingEllipses, Menu, ResizeHandle } from '@jbrowse/core/ui';
|
|
3
3
|
import { observer } from 'mobx-react';
|
|
4
4
|
import { transaction } from 'mobx';
|
|
@@ -7,7 +7,8 @@ import ImportForm from './ImportForm';
|
|
|
7
7
|
import Header from './Header';
|
|
8
8
|
import Grid from './Grid';
|
|
9
9
|
import { VerticalAxis, HorizontalAxis } from './Axes';
|
|
10
|
-
|
|
10
|
+
const TooltipWhereClicked = lazy(() => import('./DotplotTooltipClick'));
|
|
11
|
+
const TooltipWhereMouseovered = lazy(() => import('./DotplotTooltipMouseover'));
|
|
11
12
|
const blank = { left: 0, top: 0, width: 0, height: 0 };
|
|
12
13
|
const useStyles = makeStyles()(theme => ({
|
|
13
14
|
spacer: {
|
|
@@ -194,8 +195,10 @@ const DotplotViewInternal = observer(function ({ model, }) {
|
|
|
194
195
|
React.createElement(VerticalAxis, { model: model }),
|
|
195
196
|
React.createElement(HorizontalAxis, { model: model }),
|
|
196
197
|
React.createElement("div", { ref: ref, className: classes.content },
|
|
197
|
-
mouseOvered && validSelect ? (React.createElement(
|
|
198
|
-
|
|
198
|
+
mouseOvered && validSelect ? (React.createElement(Suspense, { fallback: null },
|
|
199
|
+
React.createElement(TooltipWhereMouseovered, { model: model, mouserect: mouserect, mouserectClient: mouserectClient, xdistance: xdistance }))) : null,
|
|
200
|
+
validSelect ? (React.createElement(Suspense, { fallback: null },
|
|
201
|
+
React.createElement(TooltipWhereClicked, { model: model, mousedown: mousedown, mousedownClient: mousedownClient, xdistance: xdistance, ydistance: ydistance }))) : null,
|
|
199
202
|
React.createElement("div", { style: { cursor: ctrlKeyDown ? 'pointer' : cursorMode }, onMouseDown: event => {
|
|
200
203
|
if (event.button === 0) {
|
|
201
204
|
const { clientX, clientY } = event;
|
|
@@ -194,6 +194,7 @@ export default function stateModelFactory(pm: PluginManager): import("mobx-state
|
|
|
194
194
|
trackMenuItems(): (import("@jbrowse/core/ui").MenuDivider | import("@jbrowse/core/ui").MenuSubHeader | import("@jbrowse/core/ui").NormalMenuItem | import("@jbrowse/core/ui").CheckboxMenuItem | import("@jbrowse/core/ui").RadioMenuItem | import("@jbrowse/core/ui").SubMenuItem | {
|
|
195
195
|
type: string;
|
|
196
196
|
label: string;
|
|
197
|
+
priority: number;
|
|
197
198
|
subMenu: {
|
|
198
199
|
type: string;
|
|
199
200
|
label: string;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jbrowse/plugin-dotplot-view",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.15.1",
|
|
4
4
|
"description": "JBrowse 2 dotplot view",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"jbrowse",
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
},
|
|
38
38
|
"dependencies": {
|
|
39
39
|
"@floating-ui/react": "^0.26.3",
|
|
40
|
-
"@mui/icons-material": "^
|
|
40
|
+
"@mui/icons-material": "^6.0.0",
|
|
41
41
|
"@mui/x-data-grid": "^7.0.0",
|
|
42
42
|
"@types/file-saver": "^2.0.1",
|
|
43
43
|
"clone": "^2.1.2",
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
"peerDependencies": {
|
|
47
47
|
"@jbrowse/core": "^2.0.0",
|
|
48
48
|
"@jbrowse/plugin-alignments": "^2.0.0",
|
|
49
|
-
"@mui/material": "^
|
|
49
|
+
"@mui/material": "^6.0.0",
|
|
50
50
|
"mobx": "^6.0.0",
|
|
51
51
|
"mobx-react": "^9.0.0",
|
|
52
52
|
"mobx-state-tree": "^5.0.0",
|
|
@@ -61,5 +61,5 @@
|
|
|
61
61
|
"publishConfig": {
|
|
62
62
|
"access": "public"
|
|
63
63
|
},
|
|
64
|
-
"gitHead": "
|
|
64
|
+
"gitHead": "86ed70124fc5a0b1161266659d1ca9f8796bf3fe"
|
|
65
65
|
}
|