UNPKG

molstar

Version:

A comprehensive macromolecular library.

384 lines 20.7 kB
/** * Copyright (c) 2019-2020 mol* contributors, licensed under MIT, See LICENSE file for more info. * * @author David Sehnal <david.sehnal@gmail.com> * @author Alexander Rose <alexander.rose@weirdbyte.de> */ import { __assign, __awaiter, __generator } from "tslib"; import { PluginStateObject as SO, PluginStateTransform } from '../../../../mol-plugin-state/objects'; import { VolumeServerInfo } from './model'; import { ParamDefinition as PD } from '../../../../mol-util/param-definition'; import { Task } from '../../../../mol-task'; import { urlCombine } from '../../../../mol-util/url'; import { Volume } from '../../../../mol-model/volume'; import { StateAction, StateObject, StateTransformer } from '../../../../mol-state'; import { getStreamingMethod, getIds, getContourLevel, getEmdbIds } from './util'; import { VolumeStreaming } from './behavior'; import { VolumeRepresentation3DHelpers } from '../../../../mol-plugin-state/transforms/representation'; import { VolumeRepresentationRegistry } from '../../../../mol-repr/volume/registry'; import { Theme } from '../../../../mol-theme/theme'; import { Box3D } from '../../../../mol-math/geometry'; import { Vec3 } from '../../../../mol-math/linear-algebra'; import { PluginConfig } from '../../../config'; import { Model } from '../../../../mol-model/structure'; import { GlobalModelTransformInfo } from '../../../../mol-model/structure/model/properties/global-transform'; function addEntry(entries, method, dataId, emDefaultContourLevel) { entries.push({ source: method === 'em' ? { name: 'em', params: { isoValue: Volume.IsoValue.absolute(emDefaultContourLevel || 0) } } : { name: 'x-ray', params: {} }, dataId: dataId }); } export var InitVolumeStreaming = StateAction.build({ display: { name: 'Volume Streaming' }, from: SO.Molecule.Structure, params: function (a, plugin) { var method = getStreamingMethod(a && a.data); var ids = getIds(method, a && a.data); return { method: PD.Select(method, [['em', 'EM'], ['x-ray', 'X-Ray']]), entries: PD.ObjectList({ id: PD.Text(ids[0] || '') }, function (_a) { var id = _a.id; return id; }, { defaultValue: ids.map(function (id) { return ({ id: id }); }) }), defaultView: PD.Select(method === 'em' ? 'cell' : 'selection-box', VolumeStreaming.ViewTypeOptions), options: PD.Group({ serverUrl: PD.Text(plugin.config.get(PluginConfig.VolumeStreaming.DefaultServer) || 'https://ds.litemol.org'), behaviorRef: PD.Text('', { isHidden: true }), emContourProvider: PD.Select('emdb', [['emdb', 'EMDB'], ['pdbe', 'PDBe']], { isHidden: true }), channelParams: PD.Value({}, { isHidden: true }) }) }; }, isApplicable: function (a, _, plugin) { var canStreamTest = plugin.config.get(PluginConfig.VolumeStreaming.CanStream); if (canStreamTest) return canStreamTest(a.data, plugin); return a.data.models.length === 1 && Model.probablyHasDensityMap(a.data.models[0]); } })(function (_a, plugin) { var ref = _a.ref, state = _a.state, params = _a.params; return Task.create('Volume Streaming', function (taskCtx) { return __awaiter(void 0, void 0, void 0, function () { var entries, i, il, dataId, emDefaultContourLevel, emdbIds, j, jl, emdbId, contourLevel, e_1, e_2, infoTree, info, children, infoObj, behTree; return __generator(this, function (_a) { switch (_a.label) { case 0: entries = []; i = 0, il = params.entries.length; _a.label = 1; case 1: if (!(i < il)) return [3 /*break*/, 16]; dataId = params.entries[i].id.toLowerCase(); emDefaultContourLevel = void 0; if (!(params.method === 'em')) return [3 /*break*/, 14]; if (!!dataId.toUpperCase().startsWith('EMD')) return [3 /*break*/, 11]; return [4 /*yield*/, taskCtx.update('Getting EMDB info...')]; case 2: _a.sent(); return [4 /*yield*/, getEmdbIds(plugin, taskCtx, dataId)]; case 3: emdbIds = _a.sent(); j = 0, jl = emdbIds.length; _a.label = 4; case 4: if (!(j < jl)) return [3 /*break*/, 10]; emdbId = emdbIds[j]; contourLevel = void 0; _a.label = 5; case 5: _a.trys.push([5, 7, , 8]); return [4 /*yield*/, getContourLevel(params.options.emContourProvider, plugin, taskCtx, emdbId)]; case 6: contourLevel = _a.sent(); return [3 /*break*/, 8]; case 7: e_1 = _a.sent(); console.info("Could not get map info for " + emdbId + ": " + e_1); return [3 /*break*/, 9]; case 8: addEntry(entries, params.method, emdbId, contourLevel || 0); _a.label = 9; case 9: ++j; return [3 /*break*/, 4]; case 10: return [3 /*break*/, 15]; case 11: _a.trys.push([11, 13, , 14]); return [4 /*yield*/, getContourLevel(params.options.emContourProvider, plugin, taskCtx, dataId)]; case 12: emDefaultContourLevel = _a.sent(); return [3 /*break*/, 14]; case 13: e_2 = _a.sent(); console.info("Could not get map info for " + dataId + ": " + e_2); return [3 /*break*/, 15]; case 14: addEntry(entries, params.method, dataId, emDefaultContourLevel || 0); _a.label = 15; case 15: ++i; return [3 /*break*/, 1]; case 16: infoTree = state.build().to(ref) .applyOrUpdateTagged(VolumeStreaming.RootTag, CreateVolumeStreamingInfo, { serverUrl: params.options.serverUrl, entries: entries }); return [4 /*yield*/, infoTree.commit()]; case 17: _a.sent(); info = infoTree.selector; if (!info.isOk) return [2 /*return*/]; children = state.tree.children.get(info.ref); if (!((children === null || children === void 0 ? void 0 : children.size) > 0)) return [3 /*break*/, 19]; return [4 /*yield*/, plugin.managers.structure.hierarchy.remove(children === null || children === void 0 ? void 0 : children.toArray())]; case 18: _a.sent(); _a.label = 19; case 19: infoObj = info.cell.obj; behTree = state.build().to(infoTree.ref).apply(CreateVolumeStreamingBehavior, PD.getDefaultValues(VolumeStreaming.createParams({ data: infoObj.data, defaultView: params.defaultView, channelParams: params.options.channelParams })), { ref: params.options.behaviorRef ? params.options.behaviorRef : void 0 }); if (params.method === 'em') { behTree.apply(VolumeStreamingVisual, { channel: 'em' }, { state: { isGhost: true }, tags: 'em' }); } else { behTree.apply(VolumeStreamingVisual, { channel: '2fo-fc' }, { state: { isGhost: true }, tags: '2fo-fc' }); behTree.apply(VolumeStreamingVisual, { channel: 'fo-fc(+ve)' }, { state: { isGhost: true }, tags: 'fo-fc(+ve)' }); behTree.apply(VolumeStreamingVisual, { channel: 'fo-fc(-ve)' }, { state: { isGhost: true }, tags: 'fo-fc(-ve)' }); } return [4 /*yield*/, state.updateTree(behTree).runInContext(taskCtx)]; case 20: _a.sent(); return [2 /*return*/]; } }); }); }); }); export var BoxifyVolumeStreaming = StateAction.build({ display: { name: 'Boxify Volume Streaming', description: 'Make the current box permanent.' }, from: VolumeStreaming, isApplicable: function (a) { return a.data.params.entry.params.view.name === 'selection-box'; } })(function (_a, plugin) { var a = _a.a, ref = _a.ref, state = _a.state; var params = a.data.params; if (params.entry.params.view.name !== 'selection-box') return; var box = Box3D.create(Vec3.clone(params.entry.params.view.params.bottomLeft), Vec3.clone(params.entry.params.view.params.topRight)); var r = params.entry.params.view.params.radius; Box3D.expand(box, box, Vec3.create(r, r, r)); var newParams = __assign(__assign({}, params), { entry: { name: params.entry.name, params: __assign(__assign({}, params.entry.params), { view: { name: 'box', params: { bottomLeft: box.min, topRight: box.max } } }) } }); return state.updateTree(state.build().to(ref).update(newParams)); }); var InfoEntryParams = { dataId: PD.Text(''), source: PD.MappedStatic('x-ray', { 'em': PD.Group({ isoValue: Volume.createIsoValueParam(Volume.IsoValue.relative(1)) }), 'x-ray': PD.Group({}) }) }; export { CreateVolumeStreamingInfo }; var CreateVolumeStreamingInfo = PluginStateTransform.BuiltIn({ name: 'create-volume-streaming-info', display: { name: 'Volume Streaming Info' }, from: SO.Molecule.Structure, to: VolumeServerInfo, params: function (a) { return { serverUrl: PD.Text('https://ds.litemol.org'), entries: PD.ObjectList(InfoEntryParams, function (_a) { var dataId = _a.dataId; return dataId; }, { defaultValue: [{ dataId: '', source: { name: 'x-ray', params: {} } }] }), }; } })({ apply: function (_a, plugin) { var a = _a.a, params = _a.params; return Task.create('', function (taskCtx) { return __awaiter(void 0, void 0, void 0, function () { var entries, i, il, e, dataId, emDefaultContourLevel, header, data; return __generator(this, function (_a) { switch (_a.label) { case 0: entries = []; i = 0, il = params.entries.length; _a.label = 1; case 1: if (!(i < il)) return [3 /*break*/, 5]; e = params.entries[i]; dataId = e.dataId; emDefaultContourLevel = e.source.name === 'em' ? e.source.params.isoValue : Volume.IsoValue.relative(1); return [4 /*yield*/, taskCtx.update('Getting server header...')]; case 2: _a.sent(); return [4 /*yield*/, plugin.fetch({ url: urlCombine(params.serverUrl, e.source.name + "/" + dataId.toLocaleLowerCase()), type: 'json' }).runInContext(taskCtx)]; case 3: header = _a.sent(); entries.push({ dataId: dataId, kind: e.source.name, header: header, emDefaultContourLevel: emDefaultContourLevel }); _a.label = 4; case 4: ++i; return [3 /*break*/, 1]; case 5: data = { serverUrl: params.serverUrl, entries: entries, structure: a.data }; return [2 /*return*/, new VolumeServerInfo(data, { label: 'Volume Server', description: "" + entries.map(function (e) { return e.dataId; }).join(', ') })]; } }); }); }); } }); export { CreateVolumeStreamingBehavior }; var CreateVolumeStreamingBehavior = PluginStateTransform.BuiltIn({ name: 'create-volume-streaming-behavior', display: { name: 'Volume Streaming Behavior' }, from: VolumeServerInfo, to: VolumeStreaming, params: function (a) { return VolumeStreaming.createParams({ data: a && a.data }); } })({ canAutoUpdate: function (_a) { var oldParams = _a.oldParams, newParams = _a.newParams; return oldParams.entry.params.view === newParams.entry.params.view || newParams.entry.params.view.name === 'selection-box' || newParams.entry.params.view.name === 'off'; }, apply: function (_a, plugin) { var a = _a.a, params = _a.params; return Task.create('Volume streaming', function (_) { return __awaiter(void 0, void 0, void 0, function () { var behavior; return __generator(this, function (_a) { switch (_a.label) { case 0: behavior = new VolumeStreaming.Behavior(plugin, a.data); return [4 /*yield*/, behavior.update(params)]; case 1: _a.sent(); return [2 /*return*/, new VolumeStreaming(behavior, { label: 'Volume Streaming', description: behavior.getDescription() })]; } }); }); }); }, update: function (_a) { var _this = this; var a = _a.a, b = _a.b, oldParams = _a.oldParams, newParams = _a.newParams; return Task.create('Update Volume Streaming', function (_) { return __awaiter(_this, void 0, void 0, function () { var emDefaultContourLevel, ret; return __generator(this, function (_a) { switch (_a.label) { case 0: if (oldParams.entry.name !== newParams.entry.name) { if ('em' in newParams.entry.params.channels) { emDefaultContourLevel = b.data.infoMap.get(newParams.entry.name).emDefaultContourLevel; if (emDefaultContourLevel) { newParams.entry.params.channels['em'].isoValue = emDefaultContourLevel; } } } return [4 /*yield*/, b.data.update(newParams)]; case 1: ret = (_a.sent()) ? StateTransformer.UpdateResult.Updated : StateTransformer.UpdateResult.Unchanged; b.description = b.data.getDescription(); return [2 /*return*/, ret]; } }); }); }); } }); export { VolumeStreamingVisual }; var VolumeStreamingVisual = PluginStateTransform.BuiltIn({ name: 'create-volume-streaming-visual', display: { name: 'Volume Streaming Visual' }, from: VolumeStreaming, to: SO.Volume.Representation3D, params: { channel: PD.Select('em', VolumeStreaming.ChannelTypeOptions, { isHidden: true }) } })({ apply: function (_a, plugin) { var a = _a.a, srcParams = _a.params, spine = _a.spine; return Task.create('Volume Representation', function (ctx) { return __awaiter(void 0, void 0, void 0, function () { var channel, params, provider, props, repr, structure, transform; var _a, _b; return __generator(this, function (_c) { switch (_c.label) { case 0: channel = a.data.channels[srcParams.channel]; if (!channel) return [2 /*return*/, StateObject.Null]; params = createVolumeProps(a.data, srcParams.channel); provider = VolumeRepresentationRegistry.BuiltIn.isosurface; props = params.type.params || {}; repr = provider.factory(__assign({ webgl: (_a = plugin.canvas3d) === null || _a === void 0 ? void 0 : _a.webgl }, plugin.representation.volume.themes), provider.getParams); repr.setTheme(Theme.create(plugin.representation.volume.themes, { volume: channel.data }, params)); structure = (_b = spine.getAncestorOfType(SO.Molecule.Structure)) === null || _b === void 0 ? void 0 : _b.data; transform = (structure === null || structure === void 0 ? void 0 : structure.models.length) === 0 ? void 0 : GlobalModelTransformInfo.get(structure === null || structure === void 0 ? void 0 : structure.models[0]); return [4 /*yield*/, repr.createOrUpdate(props, channel.data).runInContext(ctx)]; case 1: _c.sent(); if (transform) repr.setState({ transform: transform }); return [2 /*return*/, new SO.Volume.Representation3D({ repr: repr, sourceData: channel.data }, { label: Math.round(channel.isoValue.relativeValue * 100) / 100 + " \u03C3 [" + srcParams.channel + "]" })]; } }); }); }); }, update: function (_a, plugin) { var a = _a.a, b = _a.b, newParams = _a.newParams, spine = _a.spine; return Task.create('Volume Representation', function (ctx) { return __awaiter(void 0, void 0, void 0, function () { var channel, visible, params, props; return __generator(this, function (_a) { switch (_a.label) { case 0: channel = a.data.channels[newParams.channel]; // TODO: is this correct behavior? if (!channel) return [2 /*return*/, StateTransformer.UpdateResult.Unchanged]; visible = b.data.repr.state.visible; params = createVolumeProps(a.data, newParams.channel); props = __assign(__assign({}, b.data.repr.props), params.type.params); b.data.repr.setTheme(Theme.create(plugin.representation.volume.themes, { volume: channel.data }, params)); return [4 /*yield*/, b.data.repr.createOrUpdate(props, channel.data).runInContext(ctx)]; case 1: _a.sent(); b.data.repr.setState({ visible: visible }); b.data.sourceData = channel.data; // TODO: set the transform here as well in case the structure moves? // doing this here now breaks the code for some reason... // const structure = spine.getAncestorOfType(SO.Molecule.Structure)?.data; // const transform = structure?.models.length === 0 ? void 0 : GlobalModelTransformInfo.get(structure?.models[0]!); // if (transform) b.data.repr.setState({ transform }); return [2 /*return*/, StateTransformer.UpdateResult.Updated]; } }); }); }); } }); function createVolumeProps(streaming, channelName) { var channel = streaming.channels[channelName]; return VolumeRepresentation3DHelpers.getDefaultParamsStatic(streaming.plugin, 'isosurface', { isoValue: channel.isoValue, alpha: channel.opacity, visuals: channel.wireframe ? ['wireframe'] : ['solid'] }, 'uniform', { value: channel.color }); } //# sourceMappingURL=transformers.js.map