index.ts 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267
  1. /**
  2. * Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
  3. *
  4. * @author David Sehnal <david.sehnal@gmail.com>
  5. * @author Alexander Rose <alexander.rose@weirdbyte.de>
  6. */
  7. import { TMDETMembraneOrientation } from '../../extensions/tmdet/behavior';
  8. import { StateTransforms } from '../../mol-plugin-state/transforms';
  9. import { createPlugin } from '../../mol-plugin-ui';
  10. import { PluginUIContext } from '../../mol-plugin-ui/context';
  11. import { PluginLayoutControlsDisplay } from '../../mol-plugin/layout';
  12. import { DefaultPluginUISpec, PluginUISpec } from '../../mol-plugin-ui/spec';
  13. import { PluginConfig } from '../../mol-plugin/config';
  14. import { PluginSpec } from '../../mol-plugin/spec';
  15. import { StateBuilder, StateObjectRef } from '../../mol-state';
  16. import { Color } from '../../mol-util/color';
  17. import '../../mol-util/polyfill';
  18. import { ObjectKeys } from '../../mol-util/type-helpers';
  19. import './embedded.html';
  20. import './favicon.ico';
  21. import './index.html';
  22. import { MolScriptBuilder as MS } from '../../mol-script/language/builder';
  23. import { createStructureRepresentationParams } from '../../mol-plugin-state/helpers/structure-representation-params';
  24. import { Expression } from '../../mol-script/language/expression';
  25. import { PluginStateObject } from '../../mol-plugin-state/objects';
  26. import { MembraneOrientation } from '../../extensions/tmdet/prop';
  27. import { MEMBRANE_STORAGE_KEY } from '../../extensions/tmdet/algorithm';
  28. import { Quat, Vec3 } from '../../mol-math/linear-algebra';
  29. import { PluginCommands } from '../../mol-plugin/commands';
  30. require('mol-plugin-ui/skin/light.scss');
  31. export { PLUGIN_VERSION as version } from '../../mol-plugin/version';
  32. export { setDebugMode, setProductionMode } from '../../mol-util/debug';
  33. const Extensions = {
  34. 'tmdet-membrane-orientation': PluginSpec.Behavior(TMDETMembraneOrientation)
  35. };
  36. const DefaultViewerOptions = {
  37. extensions: ObjectKeys(Extensions),
  38. layoutIsExpanded: true,
  39. layoutShowControls: true,
  40. layoutShowRemoteState: true,
  41. layoutControlsDisplay: 'reactive' as PluginLayoutControlsDisplay,
  42. layoutShowSequence: true,
  43. layoutShowLog: true,
  44. layoutShowLeftPanel: true,
  45. collapseLeftPanel: false,
  46. disableAntialiasing: false,
  47. pixelScale: 1,
  48. enableWboit: true,
  49. viewportShowExpand: PluginConfig.Viewport.ShowExpand.defaultValue,
  50. viewportShowControls: PluginConfig.Viewport.ShowControls.defaultValue,
  51. viewportShowSettings: PluginConfig.Viewport.ShowSettings.defaultValue,
  52. viewportShowSelectionMode: PluginConfig.Viewport.ShowSelectionMode.defaultValue,
  53. viewportShowAnimation: PluginConfig.Viewport.ShowAnimation.defaultValue,
  54. pluginStateServer: PluginConfig.State.DefaultServer.defaultValue,
  55. volumeStreamingServer: PluginConfig.VolumeStreaming.DefaultServer.defaultValue,
  56. volumeStreamingDisabled: !PluginConfig.VolumeStreaming.Enabled.defaultValue,
  57. pdbProvider: PluginConfig.Download.DefaultPdbProvider.defaultValue,
  58. emdbProvider: PluginConfig.Download.DefaultEmdbProvider.defaultValue,
  59. };
  60. type ViewerOptions = typeof DefaultViewerOptions;
  61. export var membrane: MembraneOrientation;
  62. export class Viewer {
  63. plugin: PluginUIContext
  64. ////////////////////////////// UNITMP VIEWER PROTOTYPING SECTION
  65. async loadWithUNITMPMembraneRepresentation(url: string, regionDescriptors: any) {
  66. const membraneNormal: Vec3 = Vec3.fromObj(
  67. regionDescriptors['membrane-normal']
  68. );
  69. const membrane: MembraneOrientation = {
  70. planePoint1: Vec3.fromArray(Vec3.zero(), membraneNormal, 0),
  71. planePoint2: Vec3.fromArray(Vec3.zero(), membraneNormal, 0),
  72. // NOTE: centroid is not 0,0,0. It is x,y,0. Right?
  73. centroid: Vec3.fromArray(
  74. Vec3.zero(), [ membraneNormal[0], membraneNormal[1], 0 ], 0
  75. ),
  76. normalVector: membraneNormal,
  77. // TODO: radius is still just a dummy value now.
  78. // Can we send a precalculated value by our backend?
  79. //
  80. // (NOTE: the TMDET extension calculates and sets it during applying preset)
  81. radius: regionDescriptors[ 'radius' ]
  82. };
  83. membrane.planePoint2[2] *= -1;
  84. localStorage.setItem(MEMBRANE_STORAGE_KEY, JSON.stringify(membrane));
  85. const isBinary = false;
  86. const data = await this.plugin.builders.data.download({
  87. url, label: regionDescriptors['pdb-id'], isBinary
  88. }); //, { state: { isGhost: true } });
  89. const trajectory = await this.plugin.builders.structure.parseTrajectory(data, regionDescriptors.format);
  90. // create membrane representation
  91. await this.plugin.builders.structure.hierarchy.applyPreset(
  92. trajectory, 'default', { representationPreset: 'preset-membrane-orientation' as any });
  93. const structure: StateObjectRef<PluginStateObject.Molecule.Structure> =
  94. this.plugin.managers.structure.hierarchy.current.models[0].structures[0].cell;
  95. const components = {
  96. polymer: await this.plugin.builders.structure.tryCreateComponentStatic(structure, 'polymer'),
  97. ligand: await this.plugin.builders.structure.tryCreateComponentStatic(structure, 'ligand'),
  98. water: await this.plugin.builders.structure.tryCreateComponentStatic(structure, 'water'),
  99. };
  100. const builder = this.plugin.builders.structure.representation;
  101. const update = this.plugin.build();
  102. if (components.polymer) builder.buildRepresentation(update, components.polymer, { type: 'cartoon', typeParams: { alpha: 0.5 } }, { tag: 'polymer' });
  103. if (components.ligand) builder.buildRepresentation(update, components.ligand, { type: 'ball-and-stick' }, { tag: 'ligand' });
  104. if (components.water) builder.buildRepresentation(update, components.water, { type: 'ball-and-stick', typeParams: { alpha: 0.6 } }, { tag: 'water' });
  105. await update.commit();
  106. regionDescriptors.chains.forEach((chain: any) => {
  107. for(let regionKey in chain.regions) {
  108. const update = this.plugin.build();
  109. const region = chain.regions[regionKey];
  110. this.createRegionRepresentation(chain.chain_id, region, update.to(structure));
  111. update.commit();
  112. }
  113. });
  114. //
  115. // reset the camera because the membranes render 1st and the structure might not be fully visible
  116. //
  117. this.rotateCamera();
  118. //requestAnimationFrame(() => this.plugin.canvas3d?.requestCameraReset());
  119. }
  120. private createRegionRepresentation(chain: string, region: any, update: StateBuilder.To<any, any>) {
  121. const regionLabel: string = `${chain} | ${region.name}`;
  122. const color: Color = Color.fromArray(region.color, 0);
  123. const query: Expression = this.getQuery(chain, region.auth_ids as number[]);
  124. // based on https://github.com/molstar/molstar/issues/209
  125. update
  126. .apply(StateTransforms.Model.StructureSelectionFromExpression, { label: regionLabel, expression: query })
  127. .apply(StateTransforms.Representation.StructureRepresentation3D, createStructureRepresentationParams(this.plugin, update.selector.data, {
  128. color: 'uniform',
  129. colorParams: { value: color }
  130. }));
  131. }
  132. private getQuery(chainId: string, auth_array: number[]): Expression {
  133. const query: Expression =
  134. MS.struct.generator.atomGroups({
  135. 'residue-test': MS.core.set.has([MS.set( ...auth_array ), MS.ammp('auth_seq_id')]),
  136. 'chain-test': MS.core.rel.eq([chainId, MS.ammp('label_asym_id')])
  137. });
  138. return query;
  139. }
  140. private rotateCamera(): void {
  141. function rot90q(v: Vec3, axis: Vec3 = Vec3.create(1, 0, 0)): Vec3 {
  142. const q = Quat.setAxisAngle(Quat(), axis, -Math.PI/2);
  143. return Vec3.transformQuat(Vec3(), v, q);
  144. }
  145. function sub(v: Vec3, u: Vec3): Vec3 {
  146. return Vec3.sub(Vec3(), v, u);
  147. }
  148. function add(v: Vec3, u: Vec3): Vec3 {
  149. return Vec3.add(Vec3(), v, u);
  150. }
  151. const cam = this.plugin.canvas3d!.camera;
  152. const snapshot = cam.getSnapshot();
  153. const newSnapshot = {
  154. ...snapshot,
  155. // target + rotateBy90(postition - target)
  156. position: add(snapshot.target, rot90q(sub(snapshot.position, snapshot.target))),
  157. target: snapshot.target,
  158. up: Vec3.negUnitZ
  159. };
  160. PluginCommands.Camera.Reset(this.plugin, { snapshot: newSnapshot, durationMs: 1000 });
  161. //requestAnimationFrame(() => this.plugin.canvas3d?.requestCameraReset({ snapshot: newSnapshot }));
  162. }
  163. ////////////////////////////// END OF PROTOTYPING SECTION
  164. constructor(elementOrId: string | HTMLElement, options: Partial<ViewerOptions> = {}) {
  165. const o = { ...DefaultViewerOptions, ...options };
  166. const defaultSpec = DefaultPluginUISpec();
  167. const spec: PluginUISpec = {
  168. actions: defaultSpec.actions,
  169. behaviors: [
  170. ...defaultSpec.behaviors,
  171. ...o.extensions.map(e => Extensions[e]),
  172. ],
  173. animations: [...defaultSpec.animations || []],
  174. customParamEditors: defaultSpec.customParamEditors,
  175. layout: {
  176. initial: {
  177. isExpanded: o.layoutIsExpanded,
  178. showControls: o.layoutShowControls,
  179. controlsDisplay: o.layoutControlsDisplay,
  180. regionState: {
  181. bottom: 'full',
  182. left: o.collapseLeftPanel ? 'collapsed' : 'full',
  183. right: 'full',
  184. top: 'full',
  185. }
  186. },
  187. },
  188. components: {
  189. ...defaultSpec.components,
  190. controls: {
  191. ...defaultSpec.components?.controls,
  192. top: o.layoutShowSequence ? undefined : 'none',
  193. bottom: o.layoutShowLog ? undefined : 'none',
  194. left: o.layoutShowLeftPanel ? undefined : 'none',
  195. },
  196. remoteState: o.layoutShowRemoteState ? 'default' : 'none',
  197. },
  198. config: [
  199. [PluginConfig.General.DisableAntialiasing, o.disableAntialiasing],
  200. [PluginConfig.General.PixelScale, o.pixelScale],
  201. [PluginConfig.General.EnableWboit, o.enableWboit],
  202. [PluginConfig.Viewport.ShowExpand, o.viewportShowExpand],
  203. [PluginConfig.Viewport.ShowControls, o.viewportShowControls],
  204. [PluginConfig.Viewport.ShowSettings, o.viewportShowSettings],
  205. [PluginConfig.Viewport.ShowSelectionMode, o.viewportShowSelectionMode],
  206. [PluginConfig.Viewport.ShowAnimation, o.viewportShowAnimation],
  207. [PluginConfig.State.DefaultServer, o.pluginStateServer],
  208. [PluginConfig.State.CurrentServer, o.pluginStateServer],
  209. [PluginConfig.VolumeStreaming.DefaultServer, o.volumeStreamingServer],
  210. [PluginConfig.VolumeStreaming.Enabled, !o.volumeStreamingDisabled],
  211. [PluginConfig.Download.DefaultPdbProvider, o.pdbProvider],
  212. [PluginConfig.Download.DefaultEmdbProvider, o.emdbProvider]
  213. ]
  214. };
  215. const element = typeof elementOrId === 'string'
  216. ? document.getElementById(elementOrId)
  217. : elementOrId;
  218. if (!element) throw new Error(`Could not get element with id '${elementOrId}'`);
  219. this.plugin = createPlugin(element, spec);
  220. }
  221. handleResize() {
  222. this.plugin.layout.events.updated.next();
  223. }
  224. }