@jbrowse/plugin-wiggle 3.1.0 → 3.2.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/BigWigAdapter/BigWigAdapter.js +1 -1
- package/dist/LinearWiggleDisplay/model.d.ts +10 -10
- package/dist/MultiDensityRenderer/MultiDensityRenderer.js +2 -2
- package/dist/MultiLineRenderer/MultiLineRenderer.js +2 -2
- package/dist/MultiLinearWiggleDisplay/components/ClusterDialog/ClusterDialog.d.ts +6 -0
- package/dist/MultiLinearWiggleDisplay/components/ClusterDialog/ClusterDialog.js +29 -0
- package/dist/MultiLinearWiggleDisplay/components/ClusterDialog/ClusterDialogAuto.d.ts +7 -0
- package/dist/MultiLinearWiggleDisplay/components/ClusterDialog/ClusterDialogAuto.js +76 -0
- package/dist/MultiLinearWiggleDisplay/components/ClusterDialog/ClusterDialogManual.d.ts +7 -0
- package/dist/MultiLinearWiggleDisplay/components/ClusterDialog/ClusterDialogManual.js +155 -0
- package/dist/MultiLinearWiggleDisplay/components/ClusterDialog/types.d.ts +7 -0
- package/dist/MultiLinearWiggleDisplay/components/ClusterDialog/types.js +2 -0
- package/dist/MultiLinearWiggleDisplay/components/SourcesGrid.js +2 -2
- package/dist/MultiLinearWiggleDisplay/model.d.ts +15 -9
- package/dist/MultiLinearWiggleDisplay/model.js +28 -14
- package/dist/MultiRowLineRenderer/MultiRowLineRenderer.js +2 -2
- package/dist/MultiRowXYPlotRenderer/MultiRowXYPlotRenderer.js +2 -2
- package/dist/MultiXYPlotRenderer/MultiXYPlotRenderer.js +3 -3
- package/dist/WiggleBaseRenderer.d.ts +1 -0
- package/dist/WiggleBaseRenderer.js +3 -3
- package/dist/WiggleRPC/MultiWiggleClusterScoreMatrix.d.ts +14 -0
- package/dist/WiggleRPC/MultiWiggleClusterScoreMatrix.js +34 -0
- package/dist/WiggleRPC/MultiWiggleGetScoreMatrix.d.ts +2 -13
- package/dist/WiggleRPC/MultiWiggleGetScoreMatrix.js +5 -37
- package/dist/WiggleRPC/cluster.d.ts +17 -0
- package/dist/WiggleRPC/cluster.js +84 -0
- package/dist/WiggleRPC/getScoreMatrix.d.ts +6 -0
- package/dist/WiggleRPC/getScoreMatrix.js +35 -0
- package/dist/WiggleRPC/rpcMethods.d.ts +1 -0
- package/dist/WiggleRPC/rpcMethods.js +1 -0
- package/dist/WiggleRPC/type.d.ts +0 -0
- package/dist/WiggleRPC/type.js +1 -0
- package/dist/WiggleRPC/types.d.ts +13 -0
- package/dist/WiggleRPC/types.js +2 -0
- package/dist/drawDensity.d.ts +1 -0
- package/dist/drawDensity.js +12 -2
- package/dist/drawLine.d.ts +1 -0
- package/dist/drawLine.js +9 -3
- package/dist/drawXY.js +8 -8
- package/dist/index.js +1 -0
- package/dist/shared/SharedWiggleMixin.d.ts +8 -8
- package/dist/shared/SharedWiggleMixin.js +11 -8
- package/esm/BigWigAdapter/BigWigAdapter.js +1 -1
- package/esm/LinearWiggleDisplay/model.d.ts +10 -10
- package/esm/MultiDensityRenderer/MultiDensityRenderer.js +3 -3
- package/esm/MultiLineRenderer/MultiLineRenderer.js +3 -3
- package/esm/MultiLinearWiggleDisplay/components/ClusterDialog/ClusterDialog.d.ts +6 -0
- package/esm/MultiLinearWiggleDisplay/components/ClusterDialog/ClusterDialog.js +24 -0
- package/esm/MultiLinearWiggleDisplay/components/ClusterDialog/ClusterDialogAuto.d.ts +7 -0
- package/esm/MultiLinearWiggleDisplay/components/ClusterDialog/ClusterDialogAuto.js +74 -0
- package/esm/MultiLinearWiggleDisplay/components/ClusterDialog/ClusterDialogManual.d.ts +7 -0
- package/esm/MultiLinearWiggleDisplay/components/ClusterDialog/ClusterDialogManual.js +150 -0
- package/esm/MultiLinearWiggleDisplay/components/ClusterDialog/types.d.ts +7 -0
- package/esm/MultiLinearWiggleDisplay/components/ClusterDialog/types.js +1 -0
- package/esm/MultiLinearWiggleDisplay/components/SourcesGrid.js +2 -2
- package/esm/MultiLinearWiggleDisplay/model.d.ts +15 -9
- package/esm/MultiLinearWiggleDisplay/model.js +28 -14
- package/esm/MultiRowLineRenderer/MultiRowLineRenderer.js +3 -3
- package/esm/MultiRowXYPlotRenderer/MultiRowXYPlotRenderer.js +3 -3
- package/esm/MultiXYPlotRenderer/MultiXYPlotRenderer.js +4 -4
- package/esm/WiggleBaseRenderer.d.ts +1 -0
- package/esm/WiggleBaseRenderer.js +4 -4
- package/esm/WiggleRPC/MultiWiggleClusterScoreMatrix.d.ts +14 -0
- package/esm/WiggleRPC/MultiWiggleClusterScoreMatrix.js +27 -0
- package/esm/WiggleRPC/MultiWiggleGetScoreMatrix.d.ts +2 -13
- package/esm/WiggleRPC/MultiWiggleGetScoreMatrix.js +5 -37
- package/esm/WiggleRPC/cluster.d.ts +17 -0
- package/esm/WiggleRPC/cluster.js +79 -0
- package/esm/WiggleRPC/getScoreMatrix.d.ts +6 -0
- package/esm/WiggleRPC/getScoreMatrix.js +32 -0
- package/esm/WiggleRPC/rpcMethods.d.ts +1 -0
- package/esm/WiggleRPC/rpcMethods.js +1 -0
- package/esm/WiggleRPC/type.d.ts +0 -0
- package/esm/WiggleRPC/type.js +1 -0
- package/esm/WiggleRPC/types.d.ts +13 -0
- package/esm/WiggleRPC/types.js +1 -0
- package/esm/drawDensity.d.ts +1 -0
- package/esm/drawDensity.js +12 -2
- package/esm/drawLine.d.ts +1 -0
- package/esm/drawLine.js +9 -3
- package/esm/drawXY.js +8 -8
- package/esm/index.js +2 -1
- package/esm/shared/SharedWiggleMixin.d.ts +8 -8
- package/esm/shared/SharedWiggleMixin.js +11 -8
- package/package.json +5 -5
- package/dist/MultiLinearWiggleDisplay/components/ClusterDialog.d.ts +0 -11
- package/dist/MultiLinearWiggleDisplay/components/ClusterDialog.js +0 -115
- package/esm/MultiLinearWiggleDisplay/components/ClusterDialog.d.ts +0 -11
- package/esm/MultiLinearWiggleDisplay/components/ClusterDialog.js +0 -109
|
@@ -42,7 +42,7 @@ class BigWigAdapter extends BaseAdapter_1.BaseFeatureDataAdapter {
|
|
|
42
42
|
}
|
|
43
43
|
getFeatures(region, opts = {}) {
|
|
44
44
|
const { refName, start, end } = region;
|
|
45
|
-
const { bpPerPx = 0,
|
|
45
|
+
const { bpPerPx = 0, resolution = 1, stopToken, statusCallback = () => { }, } = opts;
|
|
46
46
|
return (0, rxjs_1.ObservableCreate)(async (observer) => {
|
|
47
47
|
const source = this.getConf('source');
|
|
48
48
|
const resolutionMultiplier = this.getConf('resolutionMultiplier');
|
|
@@ -258,11 +258,11 @@ declare function stateModelFactory(pluginManager: PluginManager, configSchema: A
|
|
|
258
258
|
setCrossHatches(cross: boolean): void;
|
|
259
259
|
} & {
|
|
260
260
|
readonly adapterTypeName: any;
|
|
261
|
-
readonly rendererTypeNameSimple:
|
|
261
|
+
readonly rendererTypeNameSimple: string;
|
|
262
262
|
readonly filters: undefined;
|
|
263
|
-
readonly scaleType:
|
|
264
|
-
readonly maxScore:
|
|
265
|
-
readonly minScore:
|
|
263
|
+
readonly scaleType: string;
|
|
264
|
+
readonly maxScore: number;
|
|
265
|
+
readonly minScore: number;
|
|
266
266
|
} & {
|
|
267
267
|
readonly adapterCapabilities: string[];
|
|
268
268
|
readonly rendererConfig: {
|
|
@@ -276,7 +276,7 @@ declare function stateModelFactory(pluginManager: PluginManager, configSchema: A
|
|
|
276
276
|
} & import("mobx-state-tree/dist/internal").NonEmptyObject & any & import("mobx-state-tree").IStateTreeNode<AnyConfigurationSchemaType>);
|
|
277
277
|
} & import("mobx-state-tree").IStateTreeNode<AnyConfigurationSchemaType>);
|
|
278
278
|
} & import("mobx-state-tree").IStateTreeNode<AnyConfigurationSchemaType>;
|
|
279
|
-
readonly autoscaleType:
|
|
279
|
+
readonly autoscaleType: string;
|
|
280
280
|
} & {
|
|
281
281
|
readonly domain: number[] | undefined;
|
|
282
282
|
} & {
|
|
@@ -289,9 +289,9 @@ declare function stateModelFactory(pluginManager: PluginManager, configSchema: A
|
|
|
289
289
|
scoreMin: number;
|
|
290
290
|
scoreMax: number;
|
|
291
291
|
} | undefined;
|
|
292
|
-
autoscaleType:
|
|
293
|
-
scaleType:
|
|
294
|
-
inverted:
|
|
292
|
+
autoscaleType: string;
|
|
293
|
+
scaleType: string;
|
|
294
|
+
inverted: boolean;
|
|
295
295
|
};
|
|
296
296
|
readonly canHaveFill: boolean;
|
|
297
297
|
readonly displayCrossHatchesSetting: boolean;
|
|
@@ -328,7 +328,7 @@ declare function stateModelFactory(pluginManager: PluginManager, configSchema: A
|
|
|
328
328
|
readonly rendererTypeName: string;
|
|
329
329
|
readonly quantitativeStatsRelevantToCurrentZoom: boolean;
|
|
330
330
|
readonly graphType: boolean;
|
|
331
|
-
readonly inverted:
|
|
331
|
+
readonly inverted: boolean;
|
|
332
332
|
} & {
|
|
333
333
|
adapterProps(): any;
|
|
334
334
|
readonly ticks: {
|
|
@@ -374,7 +374,7 @@ declare function stateModelFactory(pluginManager: PluginManager, configSchema: A
|
|
|
374
374
|
} | {
|
|
375
375
|
label: string;
|
|
376
376
|
type: string;
|
|
377
|
-
checked:
|
|
377
|
+
checked: boolean;
|
|
378
378
|
onClick: () => void;
|
|
379
379
|
icon?: undefined;
|
|
380
380
|
subMenu?: undefined;
|
|
@@ -40,13 +40,13 @@ const util_1 = require("@jbrowse/core/util");
|
|
|
40
40
|
const WiggleBaseRenderer_1 = __importDefault(require("../WiggleBaseRenderer"));
|
|
41
41
|
class MultiDensityPlotRenderer extends WiggleBaseRenderer_1.default {
|
|
42
42
|
async draw(ctx, props) {
|
|
43
|
-
const { sources, features } = props;
|
|
43
|
+
const { stopToken, sources, features } = props;
|
|
44
44
|
const groups = (0, util_1.groupBy)(features.values(), f => f.get('source'));
|
|
45
45
|
const height = props.height / sources.length;
|
|
46
46
|
let feats = [];
|
|
47
47
|
const { drawDensity } = await Promise.resolve().then(() => __importStar(require('../drawDensity')));
|
|
48
48
|
ctx.save();
|
|
49
|
-
|
|
49
|
+
(0, util_1.forEachWithStopTokenCheck)(sources, stopToken, source => {
|
|
50
50
|
const features = groups[source.name] || [];
|
|
51
51
|
const { reducedFeatures } = drawDensity(ctx, {
|
|
52
52
|
...props,
|
|
@@ -40,11 +40,11 @@ const util_1 = require("@jbrowse/core/util");
|
|
|
40
40
|
const WiggleBaseRenderer_1 = __importDefault(require("../WiggleBaseRenderer"));
|
|
41
41
|
class MultiLineRenderer extends WiggleBaseRenderer_1.default {
|
|
42
42
|
async draw(ctx, props) {
|
|
43
|
-
const { sources, features } = props;
|
|
43
|
+
const { stopToken, sources, features } = props;
|
|
44
44
|
const groups = (0, util_1.groupBy)(features.values(), f => f.get('source'));
|
|
45
45
|
const { drawLine } = await Promise.resolve().then(() => __importStar(require('../drawLine')));
|
|
46
46
|
let feats = [];
|
|
47
|
-
|
|
47
|
+
(0, util_1.forEachWithStopTokenCheck)(sources, stopToken, source => {
|
|
48
48
|
const { reducedFeatures } = drawLine(ctx, {
|
|
49
49
|
...props,
|
|
50
50
|
features: groups[source.name] || [],
|
|
@@ -0,0 +1,29 @@
|
|
|
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 jsx_runtime_1 = require("react/jsx-runtime");
|
|
7
|
+
const react_1 = require("react");
|
|
8
|
+
const ui_1 = require("@jbrowse/core/ui");
|
|
9
|
+
const material_1 = require("@mui/material");
|
|
10
|
+
const mobx_react_1 = require("mobx-react");
|
|
11
|
+
const ClusterDialogAuto_1 = __importDefault(require("./ClusterDialogAuto"));
|
|
12
|
+
const ClusterDialogManual_1 = __importDefault(require("./ClusterDialogManual"));
|
|
13
|
+
function Header({ activeMode, setActiveMode, }) {
|
|
14
|
+
return ((0, jsx_runtime_1.jsx)("div", { children: (0, jsx_runtime_1.jsx)(material_1.RadioGroup, { children: Object.entries({
|
|
15
|
+
auto: ((0, jsx_runtime_1.jsx)("div", { children: "Run in-app clustering (slow for large data, built in JS clustering)" })),
|
|
16
|
+
manual: ((0, jsx_runtime_1.jsx)("div", { children: "Download R script to run clustering (faster for large data, uses hclust, may be more accurate)" })),
|
|
17
|
+
}).map(([key, val]) => ((0, jsx_runtime_1.jsx)(material_1.FormControlLabel, { control: (0, jsx_runtime_1.jsx)(material_1.Radio, { checked: activeMode === key, onChange: () => {
|
|
18
|
+
setActiveMode(key);
|
|
19
|
+
} }), label: val }, key))) }) }));
|
|
20
|
+
}
|
|
21
|
+
const ClusterDialog = (0, mobx_react_1.observer)(function ({ model, handleClose, }) {
|
|
22
|
+
const [activeMode, setActiveMode] = (0, react_1.useState)('auto');
|
|
23
|
+
return ((0, jsx_runtime_1.jsx)(ui_1.Dialog, { open: true, title: "Cluster by score", maxWidth: "xl", onClose: (_, reason) => {
|
|
24
|
+
if (reason !== 'backdropClick') {
|
|
25
|
+
handleClose();
|
|
26
|
+
}
|
|
27
|
+
}, children: activeMode === 'auto' ? ((0, jsx_runtime_1.jsx)(ClusterDialogAuto_1.default, { model: model, handleClose: handleClose, children: (0, jsx_runtime_1.jsx)(Header, { activeMode: activeMode, setActiveMode: setActiveMode }) })) : ((0, jsx_runtime_1.jsx)(ClusterDialogManual_1.default, { model: model, handleClose: handleClose, children: (0, jsx_runtime_1.jsx)(Header, { activeMode: activeMode, setActiveMode: setActiveMode }) })) }));
|
|
28
|
+
});
|
|
29
|
+
exports.default = ClusterDialog;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { ReducedModel } from './types';
|
|
2
|
+
declare const ClusterDialogAuto: ({ model, children, handleClose, }: {
|
|
3
|
+
model: ReducedModel;
|
|
4
|
+
children: React.ReactNode;
|
|
5
|
+
handleClose: () => void;
|
|
6
|
+
}) => import("react/jsx-runtime").JSX.Element;
|
|
7
|
+
export default ClusterDialogAuto;
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
4
|
+
const react_1 = require("react");
|
|
5
|
+
const ui_1 = require("@jbrowse/core/ui");
|
|
6
|
+
const util_1 = require("@jbrowse/core/util");
|
|
7
|
+
const stopToken_1 = require("@jbrowse/core/util/stopToken");
|
|
8
|
+
const tracks_1 = require("@jbrowse/core/util/tracks");
|
|
9
|
+
const material_1 = require("@mui/material");
|
|
10
|
+
const mobx_react_1 = require("mobx-react");
|
|
11
|
+
const mobx_state_tree_1 = require("mobx-state-tree");
|
|
12
|
+
const ClusterDialogAuto = (0, mobx_react_1.observer)(function ({ model, children, handleClose, }) {
|
|
13
|
+
const [progress, setProgress] = (0, react_1.useState)('');
|
|
14
|
+
const [error, setError] = (0, react_1.useState)();
|
|
15
|
+
const [stopToken, setStopToken] = (0, react_1.useState)('');
|
|
16
|
+
const [showAdvanced, setShowAdvanced] = (0, react_1.useState)(false);
|
|
17
|
+
const [samplesPerPixel, setSamplesPerPixel] = (0, util_1.useLocalStorage)('cluster-samplesPerPixel', '1');
|
|
18
|
+
return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsxs)(material_1.DialogContent, { children: [children, (0, jsx_runtime_1.jsxs)("div", { style: { marginTop: 50 }, children: [(0, jsx_runtime_1.jsx)(material_1.Button, { variant: "contained", onClick: () => {
|
|
19
|
+
setShowAdvanced(!showAdvanced);
|
|
20
|
+
}, children: showAdvanced ? 'Hide advanced options' : 'Show advanced options' }), showAdvanced ? ((0, jsx_runtime_1.jsxs)("div", { style: { marginTop: 20 }, children: [(0, jsx_runtime_1.jsx)(material_1.Typography, { children: "This procedure samples the data at each 'pixel' across the visible by default" }), (0, jsx_runtime_1.jsx)(material_1.TextField, { label: "Samples per pixel (>1 for denser sampling, between 0-1 for sparser sampling)", variant: "outlined", size: "small", value: samplesPerPixel, onChange: event => {
|
|
21
|
+
setSamplesPerPixel(event.target.value);
|
|
22
|
+
} })] })) : null] }), (0, jsx_runtime_1.jsxs)("div", { children: [progress ? ((0, jsx_runtime_1.jsxs)("div", { style: { padding: 50 }, children: [(0, jsx_runtime_1.jsxs)("span", { style: { width: 400 }, children: ["Progress: ", progress] }), (0, jsx_runtime_1.jsx)(material_1.Button, { onClick: () => {
|
|
23
|
+
(0, stopToken_1.stopStopToken)(stopToken);
|
|
24
|
+
}, children: "Stop" })] })) : null, error ? (0, jsx_runtime_1.jsx)(ui_1.ErrorMessage, { error: error }) : null] })] }), (0, jsx_runtime_1.jsxs)(material_1.DialogActions, { children: [(0, jsx_runtime_1.jsx)(material_1.Button, { variant: "contained", onClick: async () => {
|
|
25
|
+
try {
|
|
26
|
+
setError(undefined);
|
|
27
|
+
setProgress('');
|
|
28
|
+
const view = (0, util_1.getContainingView)(model);
|
|
29
|
+
if (!view.initialized) {
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
const { rpcManager } = (0, util_1.getSession)(model);
|
|
33
|
+
const { sourcesWithoutLayout, adapterConfig } = model;
|
|
34
|
+
if (sourcesWithoutLayout) {
|
|
35
|
+
const sessionId = (0, tracks_1.getRpcSessionId)(model);
|
|
36
|
+
const stopToken = (0, stopToken_1.createStopToken)();
|
|
37
|
+
setStopToken(stopToken);
|
|
38
|
+
const ret = (await rpcManager.call(sessionId, 'MultiWiggleClusterScoreMatrix', {
|
|
39
|
+
regions: view.dynamicBlocks.contentBlocks,
|
|
40
|
+
sources: sourcesWithoutLayout,
|
|
41
|
+
sessionId,
|
|
42
|
+
adapterConfig,
|
|
43
|
+
stopToken,
|
|
44
|
+
bpPerPx: view.bpPerPx / +samplesPerPixel,
|
|
45
|
+
statusCallback: (arg) => {
|
|
46
|
+
setProgress(arg);
|
|
47
|
+
},
|
|
48
|
+
}));
|
|
49
|
+
model.setLayout(ret.order.map(idx => {
|
|
50
|
+
const ret = sourcesWithoutLayout[idx];
|
|
51
|
+
if (!ret) {
|
|
52
|
+
throw new Error(`out of bounds at ${idx}`);
|
|
53
|
+
}
|
|
54
|
+
return ret;
|
|
55
|
+
}));
|
|
56
|
+
}
|
|
57
|
+
handleClose();
|
|
58
|
+
}
|
|
59
|
+
catch (e) {
|
|
60
|
+
if (!(0, util_1.isAbortException)(e) && (0, mobx_state_tree_1.isAlive)(model)) {
|
|
61
|
+
console.error(e);
|
|
62
|
+
setError(e);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
finally {
|
|
66
|
+
setProgress('');
|
|
67
|
+
setStopToken('');
|
|
68
|
+
}
|
|
69
|
+
}, children: "Run clustering" }), (0, jsx_runtime_1.jsx)(material_1.Button, { variant: "contained", color: "secondary", onClick: () => {
|
|
70
|
+
handleClose();
|
|
71
|
+
if (stopToken) {
|
|
72
|
+
(0, stopToken_1.stopStopToken)(stopToken);
|
|
73
|
+
}
|
|
74
|
+
}, children: "Cancel" })] })] }));
|
|
75
|
+
});
|
|
76
|
+
exports.default = ClusterDialogAuto;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { ReducedModel } from './types';
|
|
2
|
+
declare const ClusterDialogManuals: ({ model, handleClose, children, }: {
|
|
3
|
+
model: ReducedModel;
|
|
4
|
+
handleClose: () => void;
|
|
5
|
+
children: React.ReactNode;
|
|
6
|
+
}) => import("react/jsx-runtime").JSX.Element;
|
|
7
|
+
export default ClusterDialogManuals;
|
|
@@ -0,0 +1,155 @@
|
|
|
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 jsx_runtime_1 = require("react/jsx-runtime");
|
|
7
|
+
const react_1 = require("react");
|
|
8
|
+
const ui_1 = require("@jbrowse/core/ui");
|
|
9
|
+
const util_1 = require("@jbrowse/core/util");
|
|
10
|
+
const tracks_1 = require("@jbrowse/core/util/tracks");
|
|
11
|
+
const Menu_1 = __importDefault(require("@mui/icons-material/Menu"));
|
|
12
|
+
const material_1 = require("@mui/material");
|
|
13
|
+
const copy_to_clipboard_1 = __importDefault(require("copy-to-clipboard"));
|
|
14
|
+
const file_saver_1 = require("file-saver");
|
|
15
|
+
const mobx_react_1 = require("mobx-react");
|
|
16
|
+
const mobx_state_tree_1 = require("mobx-state-tree");
|
|
17
|
+
const mui_1 = require("tss-react/mui");
|
|
18
|
+
const useStyles = (0, mui_1.makeStyles)()(theme => ({
|
|
19
|
+
textAreaFont: {
|
|
20
|
+
fontFamily: 'Courier New',
|
|
21
|
+
},
|
|
22
|
+
mgap: {
|
|
23
|
+
display: 'flex',
|
|
24
|
+
flexDirection: 'column',
|
|
25
|
+
gap: theme.spacing(4),
|
|
26
|
+
},
|
|
27
|
+
}));
|
|
28
|
+
const ClusterDialogManuals = (0, mobx_react_1.observer)(function ({ model, handleClose, children, }) {
|
|
29
|
+
const { classes } = useStyles();
|
|
30
|
+
const [paste, setPaste] = (0, react_1.useState)('');
|
|
31
|
+
const [ret, setRet] = (0, react_1.useState)();
|
|
32
|
+
const [error, setError] = (0, react_1.useState)();
|
|
33
|
+
const [loading, setLoading] = (0, react_1.useState)(false);
|
|
34
|
+
const [showAdvanced, setShowAdvanced] = (0, react_1.useState)(false);
|
|
35
|
+
const [clusterMethod, setClusterMethod] = (0, util_1.useLocalStorage)('cluster-clusterMethod', 'single');
|
|
36
|
+
const [samplesPerPixel, setSamplesPerPixel] = (0, util_1.useLocalStorage)('cluster-samplesPerPixel', '1');
|
|
37
|
+
(0, react_1.useEffect)(() => {
|
|
38
|
+
;
|
|
39
|
+
(async () => {
|
|
40
|
+
try {
|
|
41
|
+
setError(undefined);
|
|
42
|
+
setRet(undefined);
|
|
43
|
+
setLoading(true);
|
|
44
|
+
const view = (0, util_1.getContainingView)(model);
|
|
45
|
+
const { dynamicBlocks, bpPerPx } = view;
|
|
46
|
+
const { rpcManager } = (0, util_1.getSession)(model);
|
|
47
|
+
const { sourcesWithoutLayout, adapterConfig } = model;
|
|
48
|
+
const sessionId = (0, tracks_1.getRpcSessionId)(model);
|
|
49
|
+
const ret = (await rpcManager.call(sessionId, 'MultiWiggleGetScoreMatrix', {
|
|
50
|
+
regions: dynamicBlocks.contentBlocks,
|
|
51
|
+
sources: sourcesWithoutLayout,
|
|
52
|
+
sessionId,
|
|
53
|
+
adapterConfig,
|
|
54
|
+
bpPerPx: bpPerPx / +samplesPerPixel,
|
|
55
|
+
}));
|
|
56
|
+
setRet(ret);
|
|
57
|
+
}
|
|
58
|
+
catch (e) {
|
|
59
|
+
if (!(0, util_1.isAbortException)(e) && (0, mobx_state_tree_1.isAlive)(model)) {
|
|
60
|
+
console.error(e);
|
|
61
|
+
setError(e);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
finally {
|
|
65
|
+
setLoading(false);
|
|
66
|
+
}
|
|
67
|
+
})();
|
|
68
|
+
}, [model, samplesPerPixel]);
|
|
69
|
+
const results = ret
|
|
70
|
+
? `inputMatrix<-matrix(c(${Object.values(ret)
|
|
71
|
+
.map(val => val.join(','))
|
|
72
|
+
.join(',\n')}
|
|
73
|
+
),nrow=${Object.values(ret).length},byrow=TRUE)
|
|
74
|
+
rownames(inputMatrix)<-c(${Object.keys(ret)
|
|
75
|
+
.map(key => `'${key}'`)
|
|
76
|
+
.join(',')})
|
|
77
|
+
resultClusters<-hclust(dist(inputMatrix), method='${clusterMethod}')
|
|
78
|
+
cat(resultClusters$order,sep='\\n')`
|
|
79
|
+
: undefined;
|
|
80
|
+
const resultsTsv = ret
|
|
81
|
+
? Object.entries(ret)
|
|
82
|
+
.map(([key, val]) => [key, ...val].join('\t'))
|
|
83
|
+
.join('\n')
|
|
84
|
+
: undefined;
|
|
85
|
+
return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsxs)(material_1.DialogContent, { children: [children, (0, jsx_runtime_1.jsxs)("div", { style: { marginTop: 50 }, children: [(0, jsx_runtime_1.jsxs)("div", { style: {
|
|
86
|
+
display: 'flex',
|
|
87
|
+
gap: '8px',
|
|
88
|
+
flexWrap: 'wrap',
|
|
89
|
+
marginBottom: '16px',
|
|
90
|
+
}, children: [(0, jsx_runtime_1.jsx)(material_1.Button, { variant: "contained", onClick: () => {
|
|
91
|
+
(0, file_saver_1.saveAs)(new Blob([results || ''], {
|
|
92
|
+
type: 'text/plain;charset=utf-8',
|
|
93
|
+
}), 'cluster.R');
|
|
94
|
+
}, children: "Download Rscript" }), ' ', "or", ' ', (0, jsx_runtime_1.jsx)(material_1.Button, { variant: "contained", onClick: () => {
|
|
95
|
+
(0, copy_to_clipboard_1.default)(results || '');
|
|
96
|
+
}, children: "Copy Rscript to clipboard" }), ' ', (0, jsx_runtime_1.jsx)("div", { style: { float: 'right' }, children: (0, jsx_runtime_1.jsx)(ui_1.CascadingMenuButton, { menuItems: [
|
|
97
|
+
{
|
|
98
|
+
label: 'Download TSV',
|
|
99
|
+
onClick: () => {
|
|
100
|
+
(0, file_saver_1.saveAs)(new Blob([resultsTsv || ''], {
|
|
101
|
+
type: 'text/plain;charset=utf-8',
|
|
102
|
+
}), 'scores.tsv');
|
|
103
|
+
},
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
label: showAdvanced
|
|
107
|
+
? 'Hide advanced options'
|
|
108
|
+
: 'Show advanced options',
|
|
109
|
+
onClick: () => {
|
|
110
|
+
setShowAdvanced(!showAdvanced);
|
|
111
|
+
},
|
|
112
|
+
},
|
|
113
|
+
], children: (0, jsx_runtime_1.jsx)(Menu_1.default, {}) }) }), (0, jsx_runtime_1.jsx)("div", { children: showAdvanced ? ((0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)(material_1.Typography, { variant: "h6", children: "Advanced options" }), (0, jsx_runtime_1.jsx)(material_1.RadioGroup, { children: Object.entries({
|
|
114
|
+
single: 'Single',
|
|
115
|
+
complete: 'Complete',
|
|
116
|
+
}).map(([key, val]) => ((0, jsx_runtime_1.jsx)(material_1.FormControlLabel, { control: (0, jsx_runtime_1.jsx)(material_1.Radio, { checked: clusterMethod === key, onChange: () => {
|
|
117
|
+
setClusterMethod(key);
|
|
118
|
+
} }), label: val }, key))) }), (0, jsx_runtime_1.jsxs)("div", { style: { marginTop: 20 }, children: [(0, jsx_runtime_1.jsx)(material_1.Typography, { children: "This procedure samples the data at each 'pixel' across the visible by default" }), (0, jsx_runtime_1.jsx)(material_1.TextField, { label: "Samples per pixel (>1 for denser sampling, between 0-1 for sparser sampling)", variant: "outlined", size: "small", value: samplesPerPixel, onChange: event => {
|
|
119
|
+
setSamplesPerPixel(event.target.value);
|
|
120
|
+
} })] })] })) : null }), results ? ((0, jsx_runtime_1.jsx)("div", {})) : loading ? ((0, jsx_runtime_1.jsx)(ui_1.LoadingEllipses, { variant: "h6", title: "Generating score matrix" })) : error ? ((0, jsx_runtime_1.jsx)(ui_1.ErrorMessage, { error: error })) : null] }), (0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)(material_1.Typography, { variant: "subtitle2", gutterBottom: true, style: { marginTop: '16px' }, children: "Clustering Results:" }), (0, jsx_runtime_1.jsx)(material_1.TextField, { multiline: true, fullWidth: true, variant: "outlined", placeholder: "Paste results from Rscript here (sequence of numbers, one per line, specifying the new ordering)", rows: 10, value: paste, onChange: event => {
|
|
121
|
+
setPaste(event.target.value);
|
|
122
|
+
}, slotProps: {
|
|
123
|
+
input: {
|
|
124
|
+
classes: {
|
|
125
|
+
input: classes.textAreaFont,
|
|
126
|
+
},
|
|
127
|
+
},
|
|
128
|
+
} })] })] })] }), (0, jsx_runtime_1.jsxs)(material_1.DialogActions, { children: [(0, jsx_runtime_1.jsx)(material_1.Button, { variant: "contained", onClick: () => {
|
|
129
|
+
const { sourcesWithoutLayout } = model;
|
|
130
|
+
if (sourcesWithoutLayout) {
|
|
131
|
+
try {
|
|
132
|
+
model.setLayout(paste
|
|
133
|
+
.split('\n')
|
|
134
|
+
.map(t => t.trim())
|
|
135
|
+
.filter(f => !!f)
|
|
136
|
+
.map(r => +r)
|
|
137
|
+
.map(idx => {
|
|
138
|
+
const ret = sourcesWithoutLayout[idx - 1];
|
|
139
|
+
if (!ret) {
|
|
140
|
+
throw new Error(`out of bounds at ${idx}`);
|
|
141
|
+
}
|
|
142
|
+
return ret;
|
|
143
|
+
}));
|
|
144
|
+
}
|
|
145
|
+
catch (e) {
|
|
146
|
+
console.error(e);
|
|
147
|
+
(0, util_1.getSession)(model).notifyError(`${e}`, e);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
handleClose();
|
|
151
|
+
}, children: "Apply clustering" }), (0, jsx_runtime_1.jsx)(material_1.Button, { variant: "contained", color: "secondary", onClick: () => {
|
|
152
|
+
handleClose();
|
|
153
|
+
}, children: "Cancel" })] })] }));
|
|
154
|
+
});
|
|
155
|
+
exports.default = ClusterDialogManuals;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { Source } from '../../types';
|
|
2
|
+
import type { AnyConfigurationModel } from '@jbrowse/core/configuration';
|
|
3
|
+
export interface ReducedModel {
|
|
4
|
+
sourcesWithoutLayout?: Source[];
|
|
5
|
+
adapterConfig: AnyConfigurationModel;
|
|
6
|
+
setLayout: (arg: Source[]) => void;
|
|
7
|
+
}
|
|
@@ -78,12 +78,12 @@ function SourcesGrid({ rows, onChange, showTips, }) {
|
|
|
78
78
|
onChange((0, util_2.moveDown)([...rows], selected, rows.length));
|
|
79
79
|
}, disabled: !selected.length, children: [(0, jsx_runtime_1.jsx)(KeyboardDoubleArrowDown_1.default, {}), showTips ? 'Move selected items to bottom' : null] }), (0, jsx_runtime_1.jsx)(ColorPicker_1.ColorPopover, { anchorEl: anchorEl, color: widgetColor, onChange: c => {
|
|
80
80
|
setWidgetColor(c);
|
|
81
|
-
|
|
81
|
+
for (const id of selected) {
|
|
82
82
|
const elt = rows.find(f => f.name === id);
|
|
83
83
|
if (elt) {
|
|
84
84
|
elt.color = c;
|
|
85
85
|
}
|
|
86
|
-
}
|
|
86
|
+
}
|
|
87
87
|
onChange([...rows]);
|
|
88
88
|
}, onClose: () => {
|
|
89
89
|
setAnchorEl(null);
|
|
@@ -259,11 +259,11 @@ export declare function stateModelFactory(_pluginManager: PluginManager, configS
|
|
|
259
259
|
setCrossHatches(cross: boolean): void;
|
|
260
260
|
} & {
|
|
261
261
|
readonly adapterTypeName: any;
|
|
262
|
-
readonly rendererTypeNameSimple:
|
|
262
|
+
readonly rendererTypeNameSimple: string;
|
|
263
263
|
readonly filters: undefined;
|
|
264
|
-
readonly scaleType:
|
|
265
|
-
readonly maxScore:
|
|
266
|
-
readonly minScore:
|
|
264
|
+
readonly scaleType: string;
|
|
265
|
+
readonly maxScore: number;
|
|
266
|
+
readonly minScore: number;
|
|
267
267
|
} & {
|
|
268
268
|
readonly adapterCapabilities: string[];
|
|
269
269
|
readonly rendererConfig: {
|
|
@@ -277,7 +277,7 @@ export declare function stateModelFactory(_pluginManager: PluginManager, configS
|
|
|
277
277
|
} & import("mobx-state-tree/dist/internal").NonEmptyObject & any & import("mobx-state-tree").IStateTreeNode<AnyConfigurationSchemaType>);
|
|
278
278
|
} & import("mobx-state-tree").IStateTreeNode<AnyConfigurationSchemaType>);
|
|
279
279
|
} & import("mobx-state-tree").IStateTreeNode<AnyConfigurationSchemaType>;
|
|
280
|
-
readonly autoscaleType:
|
|
280
|
+
readonly autoscaleType: string;
|
|
281
281
|
} & {
|
|
282
282
|
readonly domain: number[] | undefined;
|
|
283
283
|
} & {
|
|
@@ -290,9 +290,9 @@ export declare function stateModelFactory(_pluginManager: PluginManager, configS
|
|
|
290
290
|
scoreMin: number;
|
|
291
291
|
scoreMax: number;
|
|
292
292
|
} | undefined;
|
|
293
|
-
autoscaleType:
|
|
294
|
-
scaleType:
|
|
295
|
-
inverted:
|
|
293
|
+
autoscaleType: string;
|
|
294
|
+
scaleType: string;
|
|
295
|
+
inverted: boolean;
|
|
296
296
|
};
|
|
297
297
|
readonly canHaveFill: boolean;
|
|
298
298
|
readonly displayCrossHatchesSetting: boolean;
|
|
@@ -344,6 +344,12 @@ export declare function stateModelFactory(_pluginManager: PluginManager, configS
|
|
|
344
344
|
readonly canHaveFill: boolean;
|
|
345
345
|
readonly renderColorBoxes: boolean;
|
|
346
346
|
readonly prefersOffset: boolean;
|
|
347
|
+
readonly sourcesWithoutLayout: {
|
|
348
|
+
color: string;
|
|
349
|
+
baseUri?: string;
|
|
350
|
+
name: string;
|
|
351
|
+
group?: string;
|
|
352
|
+
}[] | undefined;
|
|
347
353
|
readonly sources: {
|
|
348
354
|
color: string;
|
|
349
355
|
baseUri?: string;
|
|
@@ -354,7 +360,7 @@ export declare function stateModelFactory(_pluginManager: PluginManager, configS
|
|
|
354
360
|
} & {
|
|
355
361
|
readonly rowHeight: number;
|
|
356
362
|
readonly rowHeightTooSmallForScalebar: boolean;
|
|
357
|
-
readonly useMinimalTicks:
|
|
363
|
+
readonly useMinimalTicks: boolean;
|
|
358
364
|
} & {
|
|
359
365
|
adapterProps(): any;
|
|
360
366
|
readonly ticks: {
|
|
@@ -50,7 +50,7 @@ const util_2 = require("../util");
|
|
|
50
50
|
const randomColor = () => '#000000'.replaceAll('0', () => (~~(Math.random() * 16)).toString(16));
|
|
51
51
|
const Tooltip = (0, react_1.lazy)(() => Promise.resolve().then(() => __importStar(require('./components/Tooltip'))));
|
|
52
52
|
const SetColorDialog = (0, react_1.lazy)(() => Promise.resolve().then(() => __importStar(require('./components/SetColorDialog'))));
|
|
53
|
-
const ClusterDialog = (0, react_1.lazy)(() => Promise.resolve().then(() => __importStar(require('./components/ClusterDialog'))));
|
|
53
|
+
const ClusterDialog = (0, react_1.lazy)(() => Promise.resolve().then(() => __importStar(require('./components/ClusterDialog/ClusterDialog'))));
|
|
54
54
|
const rendererTypes = new Map([
|
|
55
55
|
['xyplot', 'MultiXYPlotRenderer'],
|
|
56
56
|
['multirowxy', 'MultiRowXYPlotRenderer'],
|
|
@@ -137,6 +137,19 @@ function stateModelFactory(_pluginManager, configSchema) {
|
|
|
137
137
|
get prefersOffset() {
|
|
138
138
|
return this.isMultiRow;
|
|
139
139
|
},
|
|
140
|
+
get sourcesWithoutLayout() {
|
|
141
|
+
var _a;
|
|
142
|
+
const sources = Object.fromEntries(((_a = self.sourcesVolatile) === null || _a === void 0 ? void 0 : _a.map(s => [s.name, s])) || []);
|
|
143
|
+
const iter = self.sourcesVolatile;
|
|
144
|
+
return iter === null || iter === void 0 ? void 0 : iter.map(s => ({
|
|
145
|
+
...sources[s.name],
|
|
146
|
+
...s,
|
|
147
|
+
})).map((s, i) => ({
|
|
148
|
+
...s,
|
|
149
|
+
color: s.color ||
|
|
150
|
+
(!this.isMultiRow ? colors_1.set1[i] || randomColor() : 'blue'),
|
|
151
|
+
}));
|
|
152
|
+
},
|
|
140
153
|
get sources() {
|
|
141
154
|
var _a;
|
|
142
155
|
const sources = Object.fromEntries(((_a = self.sourcesVolatile) === null || _a === void 0 ? void 0 : _a.map(s => [s.name, s])) || []);
|
|
@@ -164,7 +177,8 @@ function stateModelFactory(_pluginManager, configSchema) {
|
|
|
164
177
|
return this.rowHeight < 70;
|
|
165
178
|
},
|
|
166
179
|
get useMinimalTicks() {
|
|
167
|
-
return ((0, configuration_1.getConf)(self, 'minimalTicks') ||
|
|
180
|
+
return ((0, configuration_1.getConf)(self, 'minimalTicks') ||
|
|
181
|
+
this.rowHeightTooSmallForScalebar);
|
|
168
182
|
},
|
|
169
183
|
}))
|
|
170
184
|
.views(self => {
|
|
@@ -294,18 +308,6 @@ function stateModelFactory(_pluginManager, configSchema) {
|
|
|
294
308
|
},
|
|
295
309
|
]
|
|
296
310
|
: []),
|
|
297
|
-
...(self.graphType
|
|
298
|
-
? [
|
|
299
|
-
{
|
|
300
|
-
type: 'checkbox',
|
|
301
|
-
label: 'Draw cross hatches',
|
|
302
|
-
checked: self.displayCrossHatchesSetting,
|
|
303
|
-
onClick: () => {
|
|
304
|
-
self.toggleCrossHatches();
|
|
305
|
-
},
|
|
306
|
-
},
|
|
307
|
-
]
|
|
308
|
-
: []),
|
|
309
311
|
...(hasRenderings
|
|
310
312
|
? [
|
|
311
313
|
{
|
|
@@ -327,6 +329,18 @@ function stateModelFactory(_pluginManager, configSchema) {
|
|
|
327
329
|
},
|
|
328
330
|
]
|
|
329
331
|
: []),
|
|
332
|
+
...(self.graphType
|
|
333
|
+
? [
|
|
334
|
+
{
|
|
335
|
+
type: 'checkbox',
|
|
336
|
+
label: 'Draw cross hatches',
|
|
337
|
+
checked: self.displayCrossHatchesSetting,
|
|
338
|
+
onClick: () => {
|
|
339
|
+
self.toggleCrossHatches();
|
|
340
|
+
},
|
|
341
|
+
},
|
|
342
|
+
]
|
|
343
|
+
: []),
|
|
330
344
|
{
|
|
331
345
|
label: 'Cluster by score',
|
|
332
346
|
onClick: () => {
|
|
@@ -40,7 +40,7 @@ const util_1 = require("@jbrowse/core/util");
|
|
|
40
40
|
const WiggleBaseRenderer_1 = __importDefault(require("../WiggleBaseRenderer"));
|
|
41
41
|
class MultiRowLineRenderer extends WiggleBaseRenderer_1.default {
|
|
42
42
|
async draw(ctx, props) {
|
|
43
|
-
const { bpPerPx, sources, regions, features } = props;
|
|
43
|
+
const { stopToken, bpPerPx, sources, regions, features } = props;
|
|
44
44
|
const region = regions[0];
|
|
45
45
|
const groups = (0, util_1.groupBy)(features.values(), f => f.get('source'));
|
|
46
46
|
const height = props.height / sources.length;
|
|
@@ -48,7 +48,7 @@ class MultiRowLineRenderer extends WiggleBaseRenderer_1.default {
|
|
|
48
48
|
const { drawLine } = await Promise.resolve().then(() => __importStar(require('../drawLine')));
|
|
49
49
|
let feats = [];
|
|
50
50
|
ctx.save();
|
|
51
|
-
|
|
51
|
+
(0, util_1.forEachWithStopTokenCheck)(sources, stopToken, source => {
|
|
52
52
|
const { reducedFeatures } = drawLine(ctx, {
|
|
53
53
|
...props,
|
|
54
54
|
features: groups[source.name] || [],
|
|
@@ -40,7 +40,7 @@ const util_1 = require("@jbrowse/core/util");
|
|
|
40
40
|
const WiggleBaseRenderer_1 = __importDefault(require("../WiggleBaseRenderer"));
|
|
41
41
|
class MultiXYPlotRenderer extends WiggleBaseRenderer_1.default {
|
|
42
42
|
async draw(ctx, props) {
|
|
43
|
-
const { bpPerPx, sources, regions, features } = props;
|
|
43
|
+
const { stopToken, bpPerPx, sources, regions, features } = props;
|
|
44
44
|
const region = regions[0];
|
|
45
45
|
const groups = (0, util_1.groupBy)(features.values(), f => f.get('source'));
|
|
46
46
|
const height = props.height / sources.length;
|
|
@@ -48,7 +48,7 @@ class MultiXYPlotRenderer extends WiggleBaseRenderer_1.default {
|
|
|
48
48
|
const { drawXY } = await Promise.resolve().then(() => __importStar(require('../drawXY')));
|
|
49
49
|
let feats = [];
|
|
50
50
|
ctx.save();
|
|
51
|
-
|
|
51
|
+
(0, util_1.forEachWithStopTokenCheck)(sources, stopToken, source => {
|
|
52
52
|
const { reducedFeatures } = drawXY(ctx, {
|
|
53
53
|
...props,
|
|
54
54
|
features: groups[source.name] || [],
|
|
@@ -41,11 +41,11 @@ const WiggleBaseRenderer_1 = __importDefault(require("../WiggleBaseRenderer"));
|
|
|
41
41
|
const util_2 = require("../util");
|
|
42
42
|
class MultiXYPlotRenderer extends WiggleBaseRenderer_1.default {
|
|
43
43
|
async draw(ctx, props) {
|
|
44
|
-
const { sources, features } = props;
|
|
44
|
+
const { stopToken, sources, features } = props;
|
|
45
45
|
const groups = (0, util_1.groupBy)(features.values(), f => f.get('source'));
|
|
46
46
|
const { drawXY } = await Promise.resolve().then(() => __importStar(require('../drawXY')));
|
|
47
47
|
let feats = [];
|
|
48
|
-
|
|
48
|
+
(0, util_1.forEachWithStopTokenCheck)(sources, stopToken, source => {
|
|
49
49
|
const features = groups[source.name] || [];
|
|
50
50
|
const { reducedFeatures } = drawXY(ctx, {
|
|
51
51
|
...props,
|
|
@@ -54,7 +54,7 @@ class MultiXYPlotRenderer extends WiggleBaseRenderer_1.default {
|
|
|
54
54
|
colorCallback: () => source.color || 'blue',
|
|
55
55
|
});
|
|
56
56
|
feats = feats.concat(reducedFeatures);
|
|
57
|
-
}
|
|
57
|
+
});
|
|
58
58
|
return { reducedFeatures: feats };
|
|
59
59
|
}
|
|
60
60
|
}
|
|
@@ -17,6 +17,7 @@ export interface RenderArgsDeserialized extends FeatureRenderArgsDeserialized {
|
|
|
17
17
|
};
|
|
18
18
|
inverted: boolean;
|
|
19
19
|
themeOptions: ThemeOptions;
|
|
20
|
+
statusCallback?: (arg: string) => void;
|
|
20
21
|
}
|
|
21
22
|
export interface RenderArgsDeserializedWithFeatures extends RenderArgsDeserialized {
|
|
22
23
|
features: Map<string, Feature>;
|
|
@@ -12,14 +12,14 @@ class WiggleBaseRenderer extends FeatureRendererType_1.default {
|
|
|
12
12
|
}
|
|
13
13
|
async render(renderProps) {
|
|
14
14
|
const features = await this.getFeatures(renderProps);
|
|
15
|
-
const { inverted, height, regions, bpPerPx } = renderProps;
|
|
15
|
+
const { inverted, height, regions, bpPerPx, statusCallback = () => { }, } = renderProps;
|
|
16
16
|
const region = regions[0];
|
|
17
17
|
const width = (region.end - region.start) / bpPerPx;
|
|
18
|
-
const { reducedFeatures, ...rest } = await (0, util_1.renderToAbstractCanvas)(width, height, renderProps, ctx => this.draw(ctx, {
|
|
18
|
+
const { reducedFeatures, ...rest } = await (0, util_1.updateStatus)('Rendering plot', statusCallback, () => (0, util_1.renderToAbstractCanvas)(width, height, renderProps, ctx => this.draw(ctx, {
|
|
19
19
|
...renderProps,
|
|
20
20
|
features,
|
|
21
21
|
inverted,
|
|
22
|
-
}));
|
|
22
|
+
})));
|
|
23
23
|
const results = await super.render({
|
|
24
24
|
...renderProps,
|
|
25
25
|
...rest,
|