/** * Copyright (c) 2018-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 { TMDETMembraneOrientation } from '../../extensions/tmdet/behavior'; import { StateTransforms } from '../../mol-plugin-state/transforms'; import { createPlugin } from '../../mol-plugin-ui'; import { PluginUIContext } from '../../mol-plugin-ui/context'; import { PluginLayoutControlsDisplay } from '../../mol-plugin/layout'; import { DefaultPluginUISpec, PluginUISpec } from '../../mol-plugin-ui/spec'; import { PluginConfig } from '../../mol-plugin/config'; import { PluginSpec } from '../../mol-plugin/spec'; import { StateBuilder, StateObjectRef } from '../../mol-state'; import { Color } from '../../mol-util/color'; import '../../mol-util/polyfill'; import { ObjectKeys } from '../../mol-util/type-helpers'; import './embedded.html'; import './favicon.ico'; import './index.html'; import { MolScriptBuilder as MS } from '../../mol-script/language/builder'; import { createStructureRepresentationParams } from '../../mol-plugin-state/helpers/structure-representation-params'; import { Expression } from '../../mol-script/language/expression'; import { PluginStateObject } from '../../mol-plugin-state/objects'; import { MembraneOrientation } from '../../extensions/tmdet/prop'; import { MEMBRANE_STORAGE_KEY } from '../../extensions/tmdet/algorithm'; import { Vec3 } from '../../mol-math/linear-algebra'; require('mol-plugin-ui/skin/light.scss'); export { PLUGIN_VERSION as version } from '../../mol-plugin/version'; export { setDebugMode, setProductionMode } from '../../mol-util/debug'; const Extensions = { 'tmdet-membrane-orientation': PluginSpec.Behavior(TMDETMembraneOrientation) }; const DefaultViewerOptions = { extensions: ObjectKeys(Extensions), layoutIsExpanded: true, layoutShowControls: true, layoutShowRemoteState: true, layoutControlsDisplay: 'reactive' as PluginLayoutControlsDisplay, layoutShowSequence: true, layoutShowLog: true, layoutShowLeftPanel: true, collapseLeftPanel: false, disableAntialiasing: false, pixelScale: 1, enableWboit: true, viewportShowExpand: PluginConfig.Viewport.ShowExpand.defaultValue, viewportShowControls: PluginConfig.Viewport.ShowControls.defaultValue, viewportShowSettings: PluginConfig.Viewport.ShowSettings.defaultValue, viewportShowSelectionMode: PluginConfig.Viewport.ShowSelectionMode.defaultValue, viewportShowAnimation: PluginConfig.Viewport.ShowAnimation.defaultValue, pluginStateServer: PluginConfig.State.DefaultServer.defaultValue, volumeStreamingServer: PluginConfig.VolumeStreaming.DefaultServer.defaultValue, volumeStreamingDisabled: !PluginConfig.VolumeStreaming.Enabled.defaultValue, pdbProvider: PluginConfig.Download.DefaultPdbProvider.defaultValue, emdbProvider: PluginConfig.Download.DefaultEmdbProvider.defaultValue, }; type ViewerOptions = typeof DefaultViewerOptions; export var membrane: MembraneOrientation; export class Viewer { plugin: PluginUIContext ////////////////////////////// UNITMP VIEWER PROTOTYPING SECTION async loadWithUNITMPMembraneRepresentation(url: string, regionDescriptors: any) { const membraneNormal: Vec3 = Vec3.fromObj( (window as any) ['regionDescriptors']['membrane-normal'] as any ); const membrane: MembraneOrientation = { planePoint1: Vec3.fromArray(Vec3.zero(), membraneNormal, 0), planePoint2: Vec3.fromArray(Vec3.zero(), membraneNormal, 0), // NOTE: centroid is not 0,0,0. It is x,y,0. Right? centroid: Vec3.fromArray( Vec3.zero(), [ membraneNormal[0], membraneNormal[1], 0 ], 0 ), normalVector: membraneNormal, // TODO: radius is still just a dummy value now. // Can we send a precalculated value by our backend? // // (NOTE: the TMDET extension calculates and sets it during applying preset) radius: regionDescriptors[ 'radius' ] }; membrane.planePoint2[2] *= -1; window.console.debug('before store:', membrane); localStorage.setItem(MEMBRANE_STORAGE_KEY, JSON.stringify(membrane)); const isBinary = false; const data = await this.plugin.builders.data.download({ url, label: `UniTMP: ${regionDescriptors['pdb-id']}`, isBinary }); //, { state: { isGhost: true } }); const trajectory = await this.plugin.builders.structure.parseTrajectory(data, regionDescriptors.format); // create membrane representation await this.plugin.builders.structure.hierarchy.applyPreset( trajectory, 'default', { representationPreset: 'preset-membrane-orientation' as any }); const structure: StateObjectRef<PluginStateObject.Molecule.Structure> = this.plugin.managers.structure.hierarchy.current.models[0].structures[0].cell; const components = { polymer: await this.plugin.builders.structure.tryCreateComponentStatic(structure, 'polymer'), ligand: await this.plugin.builders.structure.tryCreateComponentStatic(structure, 'ligand'), water: await this.plugin.builders.structure.tryCreateComponentStatic(structure, 'water'), }; const builder = this.plugin.builders.structure.representation; const update = this.plugin.build(); if (components.polymer) builder.buildRepresentation(update, components.polymer, { type: 'cartoon', typeParams: { alpha: 0.5 } }, { tag: 'polymer' }); if (components.ligand) builder.buildRepresentation(update, components.ligand, { type: 'ball-and-stick' }, { tag: 'ligand' }); if (components.water) builder.buildRepresentation(update, components.water, { type: 'ball-and-stick', typeParams: { alpha: 0.6 } }, { tag: 'water' }); await update.commit(); regionDescriptors.chains.forEach((chain: any) => { for(let regionKey in chain.regions) { const update = this.plugin.build(); const region = chain.regions[regionKey]; let color: Color = Color.fromArray(region.color, 0); this.applyColor(chain.chain_id, region.auth_ids, update.to(structure), color); update.commit(); } }); // // reset the camera because the membranes render 1st and the structure might not be fully visible // requestAnimationFrame(() => this.plugin.canvas3d?.requestCameraReset()); } private applyColor(chain: string, auth_seq_ids: number[], update: StateBuilder.To<any, any>, color: Color) { const label: string = `${chain}.${auth_seq_ids.length}`; const query: Expression = this.getQuery(chain, auth_seq_ids); // based on https://github.com/molstar/molstar/issues/209 update .apply(StateTransforms.Model.StructureSelectionFromExpression, { label: label, expression: query }) .apply(StateTransforms.Representation.StructureRepresentation3D, createStructureRepresentationParams(this.plugin, update.selector.data, { color: 'uniform', colorParams: { value: color } })); } private getQuery(chainId: string, auth_array: number[]): Expression { const query: Expression = MS.struct.generator.atomGroups({ 'residue-test': MS.core.set.has([MS.set( ...auth_array ), MS.ammp('auth_seq_id')]), 'chain-test': MS.core.rel.eq([chainId, MS.ammp('label_asym_id')]) }); return query; } ////////////////////////////// END OF PROTOTYPING SECTION constructor(elementOrId: string | HTMLElement, options: Partial<ViewerOptions> = {}) { const o = { ...DefaultViewerOptions, ...options }; const defaultSpec = DefaultPluginUISpec(); const spec: PluginUISpec = { actions: defaultSpec.actions, behaviors: [ ...defaultSpec.behaviors, ...o.extensions.map(e => Extensions[e]), ], animations: [...defaultSpec.animations || []], customParamEditors: defaultSpec.customParamEditors, layout: { initial: { isExpanded: o.layoutIsExpanded, showControls: o.layoutShowControls, controlsDisplay: o.layoutControlsDisplay, regionState: { bottom: 'full', left: o.collapseLeftPanel ? 'collapsed' : 'full', right: 'full', top: 'full', } }, }, components: { ...defaultSpec.components, controls: { ...defaultSpec.components?.controls, top: o.layoutShowSequence ? undefined : 'none', bottom: o.layoutShowLog ? undefined : 'none', left: o.layoutShowLeftPanel ? undefined : 'none', }, remoteState: o.layoutShowRemoteState ? 'default' : 'none', }, config: [ [PluginConfig.General.DisableAntialiasing, o.disableAntialiasing], [PluginConfig.General.PixelScale, o.pixelScale], [PluginConfig.General.EnableWboit, o.enableWboit], [PluginConfig.Viewport.ShowExpand, o.viewportShowExpand], [PluginConfig.Viewport.ShowControls, o.viewportShowControls], [PluginConfig.Viewport.ShowSettings, o.viewportShowSettings], [PluginConfig.Viewport.ShowSelectionMode, o.viewportShowSelectionMode], [PluginConfig.Viewport.ShowAnimation, o.viewportShowAnimation], [PluginConfig.State.DefaultServer, o.pluginStateServer], [PluginConfig.State.CurrentServer, o.pluginStateServer], [PluginConfig.VolumeStreaming.DefaultServer, o.volumeStreamingServer], [PluginConfig.VolumeStreaming.Enabled, !o.volumeStreamingDisabled], [PluginConfig.Download.DefaultPdbProvider, o.pdbProvider], [PluginConfig.Download.DefaultEmdbProvider, o.emdbProvider] ] }; const element = typeof elementOrId === 'string' ? document.getElementById(elementOrId) : elementOrId; if (!element) throw new Error(`Could not get element with id '${elementOrId}'`); this.plugin = createPlugin(element, spec); } handleResize() { this.plugin.layout.events.updated.next(); } }