UNPKG

molstar

Version:

A comprehensive macromolecular library.

177 lines (176 loc) 9.95 kB
/** * Copyright (c) 2018-2022 mol* contributors, licensed under MIT, See LICENSE file for more info. * * @author Adam Midlik <midlik@gmail.com> */ import { Vec2 } from '../../mol-math/linear-algebra'; import { Volume } from '../../mol-model/volume'; import { createVolumeRepresentationParams } from '../../mol-plugin-state/helpers/volume-representation-params'; import { StateTransforms } from '../../mol-plugin-state/transforms'; import { Download } from '../../mol-plugin-state/transforms/data'; import { CreateGroup } from '../../mol-plugin-state/transforms/misc'; import { setSubtreeVisibility } from '../../mol-plugin/behavior/static/state'; import { PluginCommands } from '../../mol-plugin/commands'; import { Color } from '../../mol-util/color'; import { ParamDefinition as PD } from '../../mol-util/param-definition'; import { BOX, MAX_VOXELS } from './entry-root'; import { VolsegStateParams, VolumeTypeChoice } from './entry-state'; import * as ExternalAPIs from './external-api'; import { VolsegGlobalStateData } from './global-state'; const GROUP_TAG = 'volume-group'; export const VOLUME_VISUAL_TAG = 'volume-visual'; const DIRECT_VOLUME_RELATIVE_PEAK_HALFWIDTH = 0.5; ; export const SimpleVolumeParams = { volumeType: VolumeTypeChoice.PDSelect(), opacity: PD.Numeric(0.2, { min: 0, max: 1, step: 0.05 }, { hideIf: p => p.volumeType === 'off' }), }; export class VolsegVolumeData { constructor(rootData) { this.visualTypeParamCache = {}; this.entryData = rootData; } async loadVolume() { var _a, _b, _c, _d; const hasVolumes = this.entryData.metadata.raw.grid.volumes.volume_downsamplings.length > 0; if (hasVolumes) { const isoLevelPromise = ExternalAPIs.tryGetIsovalue((_a = this.entryData.metadata.raw.grid.general.source_db_id) !== null && _a !== void 0 ? _a : this.entryData.entryId); let group = (_b = this.entryData.findNodesByTags(GROUP_TAG)[0]) === null || _b === void 0 ? void 0 : _b.transform.ref; if (!group) { const newGroupNode = await this.entryData.newUpdate().apply(CreateGroup, { label: 'Volume' }, { tags: [GROUP_TAG], state: { isCollapsed: true } }).commit(); group = newGroupNode.ref; } const url = this.entryData.api.volumeUrl(this.entryData.source, this.entryData.entryId, BOX, MAX_VOXELS); const data = await this.entryData.newUpdate().to(group).apply(Download, { url, isBinary: true, label: `Volume Data: ${url}` }).commit(); const parsed = await this.entryData.plugin.dataFormats.get('dscif').parse(this.entryData.plugin, data); const volumeNode = (_d = (_c = parsed.volumes) === null || _c === void 0 ? void 0 : _c[0]) !== null && _d !== void 0 ? _d : parsed.volume; const volumeData = volumeNode.cell.obj.data; const volumeType = VolsegStateParams.volumeType.defaultValue; let isovalue = await isoLevelPromise; if (!isovalue) { const stats = volumeData.grid.stats; const maxRelative = (stats.max - stats.mean) / stats.sigma; if (maxRelative > 1) { isovalue = { kind: 'relative', value: 1.0 }; } else { isovalue = { kind: 'relative', value: maxRelative * 0.5 }; } } const adjustedIsovalue = Volume.adjustedIsoValue(volumeData, isovalue.value, isovalue.kind); const visualParams = this.createVolumeVisualParams(volumeData, volumeType); this.changeIsovalueInVolumeVisualParams(visualParams, adjustedIsovalue, volumeData.grid.stats); await this.entryData.newUpdate() .to(volumeNode) .apply(StateTransforms.Representation.VolumeRepresentation3D, visualParams, { tags: [VOLUME_VISUAL_TAG], state: { isHidden: volumeType === 'off' } }) .commit(); return { isovalue: adjustedIsovalue }; } } async setVolumeVisual(type) { var _a, _b; const visual = this.entryData.findNodesByTags(VOLUME_VISUAL_TAG)[0]; if (!visual) return; const oldParams = visual.transform.params; this.visualTypeParamCache[oldParams.type.name] = oldParams.type.params; if (type === 'off') { setSubtreeVisibility(this.entryData.plugin.state.data, visual.transform.ref, true); // true means hide, ¯\_(ツ)_/¯ } else { setSubtreeVisibility(this.entryData.plugin.state.data, visual.transform.ref, false); // true means hide, ¯\_(ツ)_/¯ if (oldParams.type.name === type) return; const newParams = { ...oldParams, type: { name: type, params: (_a = this.visualTypeParamCache[type]) !== null && _a !== void 0 ? _a : oldParams.type.params, } }; const volumeStats = (_b = visual.obj) === null || _b === void 0 ? void 0 : _b.data.sourceData.grid.stats; if (!volumeStats) throw new Error(`Cannot get volume stats from volume visual ${visual.transform.ref}`); this.changeIsovalueInVolumeVisualParams(newParams, undefined, volumeStats); const update = this.entryData.newUpdate().to(visual.transform.ref).update(newParams); await PluginCommands.State.Update(this.entryData.plugin, { state: this.entryData.plugin.state.data, tree: update, options: { doNotUpdateCurrent: true } }); } } async updateVolumeVisual(newParams) { var _a, _b; const { volumeType, opacity } = newParams; const visual = this.entryData.findNodesByTags(VOLUME_VISUAL_TAG)[0]; if (!visual) return; const oldVisualParams = visual.transform.params; this.visualTypeParamCache[oldVisualParams.type.name] = oldVisualParams.type.params; if (volumeType === 'off') { setSubtreeVisibility(this.entryData.plugin.state.data, visual.transform.ref, true); // true means hide, ¯\_(ツ)_/¯ } else { setSubtreeVisibility(this.entryData.plugin.state.data, visual.transform.ref, false); // true means hide, ¯\_(ツ)_/¯ const newVisualParams = { ...oldVisualParams, type: { name: volumeType, params: (_a = this.visualTypeParamCache[volumeType]) !== null && _a !== void 0 ? _a : oldVisualParams.type.params, } }; newVisualParams.type.params.alpha = opacity; const volumeStats = (_b = visual.obj) === null || _b === void 0 ? void 0 : _b.data.sourceData.grid.stats; if (!volumeStats) throw new Error(`Cannot get volume stats from volume visual ${visual.transform.ref}`); this.changeIsovalueInVolumeVisualParams(newVisualParams, undefined, volumeStats); const update = this.entryData.newUpdate().to(visual.transform.ref).update(newVisualParams); await PluginCommands.State.Update(this.entryData.plugin, { state: this.entryData.plugin.state.data, tree: update, options: { doNotUpdateCurrent: true } }); } } async setTryUseGpu(tryUseGpu) { const visuals = this.entryData.findNodesByTags(VOLUME_VISUAL_TAG); for (const visual of visuals) { const oldParams = visual.transform.params; if (oldParams.type.params.tryUseGpu === !tryUseGpu) { const newParams = { ...oldParams, type: { ...oldParams.type, params: { ...oldParams.type.params, tryUseGpu: tryUseGpu } } }; const update = this.entryData.newUpdate().to(visual.transform.ref).update(newParams); await PluginCommands.State.Update(this.entryData.plugin, { state: this.entryData.plugin.state.data, tree: update, options: { doNotUpdateCurrent: true } }); } } } getIsovalueFromState() { const { volumeIsovalueKind, volumeIsovalueValue } = this.entryData.currentState.value; return volumeIsovalueKind === 'relative' ? Volume.IsoValue.relative(volumeIsovalueValue) : Volume.IsoValue.absolute(volumeIsovalueValue); } createVolumeVisualParams(volume, type) { var _a; if (type === 'off') type = 'isosurface'; return createVolumeRepresentationParams(this.entryData.plugin, volume, { type: type, typeParams: { alpha: 0.2, tryUseGpu: (_a = VolsegGlobalStateData.getGlobalState(this.entryData.plugin)) === null || _a === void 0 ? void 0 : _a.tryUseGpu }, color: 'uniform', colorParams: { value: Color(0x121212) }, }); } changeIsovalueInVolumeVisualParams(params, isovalue, stats) { var _a; isovalue !== null && isovalue !== void 0 ? isovalue : (isovalue = this.getIsovalueFromState()); switch (params.type.name) { case 'isosurface': params.type.params.isoValue = isovalue; params.type.params.tryUseGpu = (_a = VolsegGlobalStateData.getGlobalState(this.entryData.plugin)) === null || _a === void 0 ? void 0 : _a.tryUseGpu; break; case 'direct-volume': const absIso = Volume.IsoValue.toAbsolute(isovalue, stats).absoluteValue; const fractIso = (absIso - stats.min) / (stats.max - stats.min); const peakHalfwidth = DIRECT_VOLUME_RELATIVE_PEAK_HALFWIDTH * stats.sigma / (stats.max - stats.min); params.type.params.controlPoints = [ Vec2.create(Math.max(fractIso - peakHalfwidth, 0), 0), Vec2.create(fractIso, 1), Vec2.create(Math.min(fractIso + peakHalfwidth, 1), 0), ]; break; } } }