@jbrowse/plugin-wiggle
Version:
JBrowse 2 wiggle adapters, tracks, etc.
308 lines (307 loc) • 11.5 kB
JavaScript
import { lazy } from 'react';
import { ConfigurationReference, getConf, readConfObject, } from '@jbrowse/core/configuration';
import { getEnv, getSession, isSelectionContainer } from '@jbrowse/core/util';
import { stopStopToken } from '@jbrowse/core/util/stopToken';
import { BaseLinearDisplay } from '@jbrowse/plugin-linear-genome-view';
import { types } from 'mobx-state-tree';
import { getNiceDomain } from '../util';
const SetMinMaxDialog = lazy(() => import('./SetMinMaxDialog'));
export default function SharedWiggleMixin(configSchema) {
return types
.compose(BaseLinearDisplay, types.model({
selectedRendering: types.optional(types.string, ''),
resolution: types.optional(types.number, 1),
fill: types.maybe(types.boolean),
minSize: types.maybe(types.number),
color: types.maybe(types.string),
posColor: types.maybe(types.string),
negColor: types.maybe(types.string),
summaryScoreMode: types.maybe(types.string),
rendererTypeNameState: types.maybe(types.string),
scale: types.maybe(types.string),
autoscale: types.maybe(types.string),
displayCrossHatches: types.maybe(types.boolean),
constraints: types.optional(types.model({
max: types.maybe(types.number),
min: types.maybe(types.number),
}), {}),
configuration: ConfigurationReference(configSchema),
}))
.volatile(() => ({
message: undefined,
stats: undefined,
statsFetchInProgress: undefined,
}))
.actions(self => ({
updateQuantitativeStats(stats) {
const { currStatsBpPerPx, scoreMin, scoreMax } = stats;
const EPSILON = 0.000001;
if (!self.stats ||
Math.abs(self.stats.scoreMax - scoreMax) > EPSILON ||
Math.abs(self.stats.scoreMin - scoreMin) > EPSILON) {
self.stats = {
currStatsBpPerPx,
scoreMin,
scoreMax,
};
}
},
setColor(color) {
self.color = color;
},
setPosColor(color) {
self.posColor = color;
},
setNegColor(color) {
self.negColor = color;
},
setStatsLoading(arg) {
if (self.statsFetchInProgress) {
stopStopToken(self.statsFetchInProgress);
}
self.statsFetchInProgress = arg;
},
selectFeature(feature) {
const session = getSession(self);
if (isSelectionContainer(session)) {
session.setSelection(feature);
}
},
setResolution(res) {
self.resolution = res;
},
setFill(fill) {
if (fill === 0) {
self.fill = true;
self.minSize = 0;
}
else if (fill === 1) {
self.fill = false;
self.minSize = 1;
}
else if (fill === 2) {
self.fill = false;
self.minSize = 2;
}
},
toggleLogScale() {
self.scale = self.scale === 'log' ? 'linear' : 'log';
},
setScaleType(scale) {
self.scale = scale;
},
setSummaryScoreMode(val) {
self.summaryScoreMode = val;
},
setAutoscale(val) {
self.autoscale = val;
},
setMaxScore(val) {
self.constraints.max = val;
},
setRendererType(val) {
self.rendererTypeNameState = val;
},
setMinScore(val) {
self.constraints.min = val;
},
toggleCrossHatches() {
self.displayCrossHatches = !self.displayCrossHatches;
},
setCrossHatches(cross) {
self.displayCrossHatches = cross;
},
}))
.views(self => ({
get adapterTypeName() {
return self.adapterConfig.type;
},
get rendererTypeNameSimple() {
var _a;
return ((_a = self.rendererTypeNameState) !== null && _a !== void 0 ? _a : getConf(self, 'defaultRendering'));
},
get filters() {
return undefined;
},
get scaleType() {
var _a;
return (_a = self.scale) !== null && _a !== void 0 ? _a : getConf(self, 'scaleType');
},
get maxScore() {
var _a;
return (_a = self.constraints.max) !== null && _a !== void 0 ? _a : getConf(self, 'maxScore');
},
get minScore() {
var _a;
return (_a = self.constraints.min) !== null && _a !== void 0 ? _a : getConf(self, 'minScore');
},
}))
.views(self => ({
get adapterCapabilities() {
const type = self.adapterTypeName;
const { pluginManager } = getEnv(self);
return pluginManager.getAdapterType(type).adapterCapabilities;
},
get rendererConfig() {
const { color, displayCrossHatches, fill, minSize, negColor, posColor, summaryScoreMode, scaleType, rendererTypeName, } = self;
const configBlob = getConf(self, ['renderers', rendererTypeName]) || {};
return self.rendererType.configSchema.create({
...configBlob,
...(scaleType ? { scaleType } : {}),
...(fill !== undefined ? { filled: fill } : {}),
...(displayCrossHatches !== undefined
? { displayCrossHatches }
: {}),
...(summaryScoreMode !== undefined ? { summaryScoreMode } : {}),
...(color !== undefined ? { color } : {}),
...(negColor !== undefined ? { negColor } : {}),
...(posColor !== undefined ? { posColor } : {}),
...(minSize !== undefined ? { minSize } : {}),
}, getEnv(self));
},
get autoscaleType() {
var _a;
return (_a = self.autoscale) !== null && _a !== void 0 ? _a : getConf(self, 'autoscale');
},
}))
.views(self => {
let oldDomain = [0, 0];
return {
get domain() {
const { stats, scaleType, minScore, maxScore } = self;
if (!stats) {
return undefined;
}
const ret = getNiceDomain({
domain: [stats.scoreMin, stats.scoreMax],
bounds: [minScore, maxScore],
scaleType,
});
if (scaleType === 'log' && ret[1] === Number.MIN_VALUE) {
return [0, Number.MIN_VALUE];
}
if (JSON.stringify(oldDomain) !== JSON.stringify(ret)) {
oldDomain = ret;
}
return oldDomain;
},
};
})
.views(self => ({
get filled() {
var _a;
return ((_a = self.fill) !== null && _a !== void 0 ? _a : readConfObject(self.rendererConfig, 'filled'));
},
get summaryScoreModeSetting() {
var _a;
return ((_a = self.summaryScoreMode) !== null && _a !== void 0 ? _a : readConfObject(self.rendererConfig, 'summaryScoreMode'));
},
get scaleOpts() {
return {
domain: self.domain,
stats: self.stats,
autoscaleType: self.autoscaleType,
scaleType: self.scaleType,
inverted: getConf(self, 'inverted'),
};
},
get canHaveFill() {
return self.rendererTypeName === 'XYPlotRenderer';
},
get displayCrossHatchesSetting() {
var _a;
return ((_a = self.displayCrossHatches) !== null && _a !== void 0 ? _a : readConfObject(self.rendererConfig, 'displayCrossHatches'));
},
get hasResolution() {
return self.adapterCapabilities.includes('hasResolution');
},
get hasGlobalStats() {
return self.adapterCapabilities.includes('hasGlobalStats');
},
}))
.views(self => ({
scoreTrackMenuItems() {
return [
...(self.hasResolution
? [
{
label: 'Resolution',
subMenu: [
{
label: 'Finer resolution',
onClick: () => {
self.setResolution(self.resolution * 5);
},
},
{
label: 'Coarser resolution',
onClick: () => {
self.setResolution(self.resolution / 5);
},
},
],
},
{
label: 'Summary score mode',
subMenu: ['min', 'max', 'avg', 'whiskers'].map(elt => ({
label: elt,
type: 'radio',
checked: self.summaryScoreModeSetting === elt,
onClick: () => {
self.setSummaryScoreMode(elt);
},
})),
},
]
: []),
{
label: self.scaleType === 'log' ? 'Set linear scale' : 'Set log scale',
onClick: () => {
self.toggleLogScale();
},
},
{
label: 'Autoscale type',
subMenu: [
['local', 'Local'],
...(self.hasGlobalStats
? [
['global', 'Global'],
['globalsd', 'Global ± 3σ'],
]
: []),
['localsd', 'Local ± 3σ'],
].map(([val, label]) => ({
label,
type: 'radio',
checked: self.autoscaleType === val,
onClick: () => {
self.setAutoscale(val);
},
})),
},
{
label: 'Set min/max score',
onClick: () => {
getSession(self).queueDialog(handleClose => [
SetMinMaxDialog,
{
model: self,
handleClose,
},
]);
},
},
];
},
}))
.actions(self => {
const { reload: superReload } = self;
return {
async reload() {
self.setError();
superReload();
},
};
});
}