@jbrowse/plugin-dotplot-view 2.15.0 → 2.15.2

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 (29) hide show
  1. package/dist/DotplotDisplay/stateModelFactory.d.ts +6 -1
  2. package/dist/DotplotRenderer/DotplotRenderer.d.ts +1 -9
  3. package/dist/DotplotRenderer/DotplotRenderer.js +25 -214
  4. package/dist/DotplotRenderer/drawDotplot.d.ts +21 -0
  5. package/dist/DotplotRenderer/drawDotplot.js +216 -0
  6. package/dist/DotplotView/components/{DotplotTooltip.d.ts → DotplotTooltipClick.d.ts} +2 -8
  7. package/dist/DotplotView/components/DotplotTooltipClick.js +21 -0
  8. package/dist/DotplotView/components/DotplotTooltipMouseover.d.ts +10 -0
  9. package/dist/DotplotView/components/DotplotTooltipMouseover.js +20 -0
  10. package/dist/DotplotView/components/DotplotView.js +6 -3
  11. package/dist/DotplotView/components/ImportForm/ImportCustomTrack.d.ts +2 -2
  12. package/dist/DotplotView/components/ImportForm/ImportCustomTrack.js +27 -6
  13. package/dist/DotplotView/model.d.ts +1 -0
  14. package/esm/DotplotDisplay/stateModelFactory.d.ts +6 -1
  15. package/esm/DotplotRenderer/DotplotRenderer.d.ts +1 -9
  16. package/esm/DotplotRenderer/DotplotRenderer.js +2 -214
  17. package/esm/DotplotRenderer/drawDotplot.d.ts +21 -0
  18. package/esm/DotplotRenderer/drawDotplot.js +213 -0
  19. package/esm/DotplotView/components/{DotplotTooltip.d.ts → DotplotTooltipClick.d.ts} +2 -8
  20. package/esm/DotplotView/components/DotplotTooltipClick.js +15 -0
  21. package/esm/DotplotView/components/DotplotTooltipMouseover.d.ts +10 -0
  22. package/esm/DotplotView/components/DotplotTooltipMouseover.js +15 -0
  23. package/esm/DotplotView/components/DotplotView.js +7 -4
  24. package/esm/DotplotView/components/ImportForm/ImportCustomTrack.d.ts +2 -2
  25. package/esm/DotplotView/components/ImportForm/ImportCustomTrack.js +27 -6
  26. package/esm/DotplotView/model.d.ts +1 -0
  27. package/package.json +2 -2
  28. package/dist/DotplotView/components/DotplotTooltip.js +0 -84
  29. package/esm/DotplotView/components/DotplotTooltip.js +0 -78
@@ -28,7 +28,7 @@ const material_1 = require("@mui/material");
28
28
  const ui_1 = require("@jbrowse/core/ui");
29
29
  const mobx_react_1 = require("mobx-react");
30
30
  const util_1 = require("./util");
31
- function getAdapter({ radioOption, assembly1, assembly2, fileLocation, bed1Location, bed2Location, }) {
31
+ function getAdapter({ radioOption, assembly1, assembly2, fileLocation, indexFileLocation, bed1Location, bed2Location, }) {
32
32
  if (radioOption === '.paf') {
33
33
  return {
34
34
  type: 'PAFAdapter',
@@ -79,14 +79,23 @@ function getAdapter({ radioOption, assembly1, assembly2, fileLocation, bed1Locat
79
79
  assemblyNames: [assembly1, assembly2],
80
80
  };
81
81
  }
82
+ else if (radioOption === '.pif.gz') {
83
+ return {
84
+ type: 'PairwiseIndexedPAFAdapter',
85
+ pifGzLocation: fileLocation,
86
+ index: { location: indexFileLocation },
87
+ assemblyNames: [assembly1, assembly2],
88
+ };
89
+ }
82
90
  else {
83
91
  throw new Error(`Unknown to detect type ${radioOption} from filename (select radio button to clarify)`);
84
92
  }
85
93
  }
86
- const OpenTrack = (0, mobx_react_1.observer)(({ assembly1, assembly2, setSessionTrackData, }) => {
94
+ const ImportCustomTrack = (0, mobx_react_1.observer)(function ({ assembly1, assembly2, setSessionTrackData, }) {
87
95
  const [bed2Location, setBed2Location] = (0, react_1.useState)();
88
96
  const [bed1Location, setBed1Location] = (0, react_1.useState)();
89
97
  const [fileLocation, setFileLocation] = (0, react_1.useState)();
98
+ const [indexFileLocation, setIndexFileLocation] = (0, react_1.useState)();
90
99
  const [value, setValue] = (0, react_1.useState)('');
91
100
  const [error, setError] = (0, react_1.useState)();
92
101
  const fileName = (0, util_1.getName)(fileLocation);
@@ -107,6 +116,7 @@ const OpenTrack = (0, mobx_react_1.observer)(({ assembly1, assembly2, setSession
107
116
  assembly1,
108
117
  assembly2,
109
118
  fileLocation,
119
+ indexFileLocation,
110
120
  bed1Location,
111
121
  bed2Location,
112
122
  }),
@@ -124,12 +134,13 @@ const OpenTrack = (0, mobx_react_1.observer)(({ assembly1, assembly2, setSession
124
134
  bed1Location,
125
135
  bed2Location,
126
136
  fileLocation,
137
+ indexFileLocation,
127
138
  radioOption,
128
139
  setSessionTrackData,
129
140
  ]);
130
141
  return (react_1.default.createElement(material_1.Paper, { style: { padding: 12 } },
131
142
  error ? react_1.default.createElement(ui_1.ErrorMessage, { error: error }) : null,
132
- react_1.default.createElement(material_1.Typography, { style: { textAlign: 'center' } }, "Add a .paf, .out (MashMap), .delta (Mummer), .chain, .anchors or .anchors.simple (MCScan) file to view in the dotplot. These file types can also be gzipped. The first assembly should be the query sequence (e.g. left column of the PAF) and the second assembly should be the target sequence (e.g. right column of the PAF)"),
143
+ react_1.default.createElement(material_1.Typography, { style: { textAlign: 'center' } }, "Add a .paf, .out (MashMap), .delta (Mummer), .chain, .anchors or .anchors.simple (MCScan) file to view. These file types can also be gzipped. The first assembly should be the query sequence (e.g. left column of the PAF) and the second assembly should be the target sequence (e.g. right column of the PAF)"),
133
144
  react_1.default.createElement(material_1.RadioGroup, { value: radioOption, onChange: event => {
134
145
  setValue(event.target.value);
135
146
  } },
@@ -145,7 +156,9 @@ const OpenTrack = (0, mobx_react_1.observer)(({ assembly1, assembly2, setSession
145
156
  react_1.default.createElement(material_1.Grid, { item: true },
146
157
  react_1.default.createElement(material_1.FormControlLabel, { value: ".anchors", control: react_1.default.createElement(material_1.Radio, null), label: ".anchors" })),
147
158
  react_1.default.createElement(material_1.Grid, { item: true },
148
- react_1.default.createElement(material_1.FormControlLabel, { value: ".anchors.simple", control: react_1.default.createElement(material_1.Radio, null), label: ".anchors.simple" })))),
159
+ react_1.default.createElement(material_1.FormControlLabel, { value: ".anchors.simple", control: react_1.default.createElement(material_1.Radio, null), label: ".anchors.simple" })),
160
+ react_1.default.createElement(material_1.Grid, { item: true },
161
+ react_1.default.createElement(material_1.FormControlLabel, { value: ".pif.gz", control: react_1.default.createElement(material_1.Radio, null), label: ".pif.gz" })))),
149
162
  react_1.default.createElement(material_1.Grid, { container: true, justifyContent: "center" },
150
163
  react_1.default.createElement(material_1.Grid, { item: true }, value === '.anchors' || value === '.anchors.simple' ? (react_1.default.createElement("div", null,
151
164
  react_1.default.createElement("div", { style: { margin: 20 } },
@@ -166,8 +179,16 @@ const OpenTrack = (0, mobx_react_1.observer)(({ assembly1, assembly2, setSession
166
179
  react_1.default.createElement("div", null,
167
180
  react_1.default.createElement(ui_1.FileSelector, { name: "genome 2 .bed (right column of anchors file)", description: "", location: bed2Location, setLocation: loc => {
168
181
  setBed2Location(loc);
169
- } }))))) : (react_1.default.createElement(ui_1.FileSelector, { name: value ? `${value} location` : '', description: "", location: fileLocation, setLocation: loc => {
182
+ } }))))) : value === '.pif.gz' ? (react_1.default.createElement("div", { style: { display: 'flex' } },
183
+ react_1.default.createElement("div", null,
184
+ react_1.default.createElement(ui_1.FileSelector, { name: `${value} location`, description: "", location: fileLocation, setLocation: loc => {
185
+ setFileLocation(loc);
186
+ } })),
187
+ react_1.default.createElement("div", null,
188
+ react_1.default.createElement(ui_1.FileSelector, { name: `${value} index location`, description: "", location: indexFileLocation, setLocation: loc => {
189
+ setIndexFileLocation(loc);
190
+ } })))) : (react_1.default.createElement(ui_1.FileSelector, { name: value ? `${value} location` : '', description: "", location: fileLocation, setLocation: loc => {
170
191
  setFileLocation(loc);
171
192
  } }))))));
172
193
  });
173
- exports.default = OpenTrack;
194
+ exports.default = ImportCustomTrack;
@@ -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;
@@ -25,7 +25,12 @@ export declare function stateModelFactory(configSchema: AnyConfigurationSchemaTy
25
25
  error: unknown;
26
26
  message: string | undefined;
27
27
  } & {
28
- readonly RenderingComponent: React.FC<{
28
+ readonly RenderingComponent: React.
29
+ /**
30
+ * #stateModel DotplotDisplay
31
+ * #category display
32
+ */
33
+ FC<{
29
34
  model: {
30
35
  id: string;
31
36
  type: string;
@@ -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: import("react").JSX.Element;
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 ret = await renderToAbstractCanvas(width, height, renderProps, ctx => this.drawDotplot(ctx, { ...renderProps, views }));
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 TooltipWhereMouseovered: ({ model, mouserect, mouserectClient, xdistance, }: {
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;