UNPKG

@jbrowse/plugin-wiggle

Version:

JBrowse 2 wiggle adapters, tracks, etc.

351 lines (350 loc) 13.3 kB
import { lazy } from 'react'; import { ConfigurationReference, getConf } from '@jbrowse/core/configuration'; import { getEnv, getSession, isSelectionContainer } from '@jbrowse/core/util'; import { stopStopToken } from '@jbrowse/core/util/stopToken'; import { types } from '@jbrowse/mobx-state-tree'; import { BaseLinearDisplay } from '@jbrowse/plugin-linear-genome-view'; import { getNiceDomain } from "../util.js"; const SetMinMaxDialog = lazy(() => import("./SetMinMaxDialog.js")); 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, statsRegion) { const { currStatsBpPerPx, scoreMin, scoreMax, scoreMeanMin, scoreMeanMax, } = stats; const EPSILON = 0.000001; if (!self.stats || Math.abs(self.stats.scoreMax - scoreMax) > EPSILON || Math.abs(self.stats.scoreMin - scoreMin) > EPSILON || self.stats.statsRegion !== statsRegion) { self.stats = { currStatsBpPerPx, scoreMin, scoreMax, scoreMeanMin, scoreMeanMax, statsRegion, }; } }, 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() { return (self.rendererTypeNameState ?? getConf(self, 'defaultRendering')); }, get filters() { return undefined; }, get scaleType() { return self.scale ?? getConf(self, 'scaleType'); }, get maxScore() { return self.constraints.max ?? getConf(self, 'maxScore'); }, get minScore() { return self.constraints.min ?? 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, scale, rendererTypeName, } = self; const conf = self.configuration.renderers?.[rendererTypeName]; return { scaleType: scale ?? conf?.scaleType?.value, filled: fill ?? conf?.filled?.value, displayCrossHatches: displayCrossHatches ?? conf?.displayCrossHatches?.value, summaryScoreMode: summaryScoreMode ?? conf?.summaryScoreMode?.value, color: color ?? conf?.color?.value, negColor: negColor ?? conf?.negColor?.value, posColor: posColor ?? conf?.posColor?.value, minSize: minSize ?? conf?.minSize?.value, }; }, get autoscaleType() { return self.autoscale ?? getConf(self, 'autoscale'); }, })) .views(self => { let oldDomain = [0, 0]; return { get domain() { const { stats, scaleType, minScore, maxScore, rendererConfig } = self; if (!stats) { return undefined; } const { summaryScoreMode } = rendererConfig; const useMeanStats = summaryScoreMode === 'mean' || summaryScoreMode === 'avg'; const domainMin = useMeanStats ? (stats.scoreMeanMin ?? stats.scoreMin) : stats.scoreMin; const domainMax = useMeanStats ? (stats.scoreMeanMax ?? stats.scoreMax) : stats.scoreMax; const ret = getNiceDomain({ domain: [domainMin, domainMax], 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() { return self.rendererConfig.filled; }, get summaryScoreModeSetting() { return 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() { return 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: 'Scale type', subMenu: [ { label: 'Linear scale', type: 'radio', checked: self.scaleType === 'linear', onClick: () => { self.setScaleType('linear'); }, }, { label: 'Log scale', type: 'radio', checked: self.scaleType === 'log', onClick: () => { self.setScaleType('log'); }, }, ], }, { 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(); }, }; }) .postProcessSnapshot(snap => { if (!snap) { return snap; } const { selectedRendering, resolution, fill, minSize, color, posColor, negColor, summaryScoreMode, rendererTypeNameState, scale, autoscale, displayCrossHatches, constraints, ...rest } = snap; return { ...rest, ...(selectedRendering ? { selectedRendering } : {}), ...(resolution !== 1 ? { resolution } : {}), ...(fill !== undefined ? { fill } : {}), ...(minSize !== undefined ? { minSize } : {}), ...(color !== undefined ? { color } : {}), ...(posColor !== undefined ? { posColor } : {}), ...(negColor !== undefined ? { negColor } : {}), ...(summaryScoreMode !== undefined ? { summaryScoreMode } : {}), ...(rendererTypeNameState !== undefined ? { rendererTypeNameState } : {}), ...(scale !== undefined ? { scale } : {}), ...(autoscale !== undefined ? { autoscale } : {}), ...(displayCrossHatches !== undefined ? { displayCrossHatches } : {}), ...(constraints.min !== undefined || constraints.max !== undefined ? { constraints } : {}), }; }); }