index.ts 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  1. /**
  2. * Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
  3. *
  4. * @author David Sehnal <david.sehnal@gmail.com>
  5. */
  6. import { createPlugin, DefaultPluginSpec } from 'mol-plugin';
  7. import './index.html'
  8. import { PluginContext } from 'mol-plugin/context';
  9. import { PluginCommands } from 'mol-plugin/command';
  10. import { StateTransforms } from 'mol-plugin/state/transforms';
  11. import { StructureRepresentation3DHelpers } from 'mol-plugin/state/transforms/representation';
  12. import { Color } from 'mol-util/color';
  13. import { PluginStateObject as PSO, PluginStateObject } from 'mol-plugin/state/objects';
  14. import { AnimateModelIndex } from 'mol-plugin/state/animation/built-in';
  15. import { StateBuilder, StateObject } from 'mol-state';
  16. import { EvolutionaryConservation } from './annotation';
  17. import { LoadParams, SupportedFormats, RepresentationStyle, ModelInfo } from './helpers';
  18. import { RxEventHelper } from 'mol-util/rx-event-helper';
  19. import { ControlsWrapper } from './ui/controls';
  20. import { PluginState } from 'mol-plugin/state';
  21. require('mol-plugin/skin/light.scss')
  22. class MolStarProteopediaWrapper {
  23. static VERSION_MAJOR = 2;
  24. static VERSION_MINOR = 0;
  25. private _ev = RxEventHelper.create();
  26. readonly events = {
  27. modelInfo: this._ev<ModelInfo>()
  28. };
  29. plugin: PluginContext;
  30. init(target: string | HTMLElement) {
  31. this.plugin = createPlugin(typeof target === 'string' ? document.getElementById(target)! : target, {
  32. ...DefaultPluginSpec,
  33. layout: {
  34. initial: {
  35. isExpanded: false,
  36. showControls: false
  37. },
  38. controls: {
  39. right: ControlsWrapper
  40. }
  41. }
  42. });
  43. this.plugin.structureRepresentation.themeCtx.colorThemeRegistry.add(EvolutionaryConservation.Descriptor.name, EvolutionaryConservation.colorTheme!);
  44. this.plugin.lociLabels.addProvider(EvolutionaryConservation.labelProvider);
  45. this.plugin.customModelProperties.register(EvolutionaryConservation.propertyProvider);
  46. }
  47. get state() {
  48. return this.plugin.state.dataState;
  49. }
  50. private download(b: StateBuilder.To<PSO.Root>, url: string) {
  51. return b.apply(StateTransforms.Data.Download, { url, isBinary: false })
  52. }
  53. private model(b: StateBuilder.To<PSO.Data.Binary | PSO.Data.String>, format: SupportedFormats, assemblyId: string) {
  54. const parsed = format === 'cif'
  55. ? b.apply(StateTransforms.Data.ParseCif).apply(StateTransforms.Model.TrajectoryFromMmCif)
  56. : b.apply(StateTransforms.Model.TrajectoryFromPDB);
  57. return parsed
  58. .apply(StateTransforms.Model.ModelFromTrajectory, { modelIndex: 0 }, { ref: 'model' });
  59. }
  60. private structure(assemblyId: string) {
  61. const model = this.state.build().to('model');
  62. return model
  63. .apply(StateTransforms.Model.CustomModelProperties, { properties: [EvolutionaryConservation.Descriptor.name] }, { ref: 'props', props: { isGhost: false } })
  64. .apply(StateTransforms.Model.StructureAssemblyFromModel, { id: assemblyId || 'deposited' }, { ref: 'asm' });
  65. }
  66. private visual(ref: string, style?: RepresentationStyle) {
  67. const structure = this.getObj<PluginStateObject.Molecule.Structure>(ref);
  68. if (!structure) return;
  69. const root = this.state.build().to(ref);
  70. root.apply(StateTransforms.Model.StructureComplexElement, { type: 'atomic-sequence' }, { ref: 'sequence' })
  71. .apply(StateTransforms.Representation.StructureRepresentation3D,
  72. StructureRepresentation3DHelpers.getDefaultParamsWithTheme(this.plugin,
  73. (style && style.sequence && style.sequence.kind) || 'cartoon',
  74. (style && style.sequence && style.sequence.coloring) || 'unit-index', structure),
  75. { ref: 'sequence-visual' });
  76. root.apply(StateTransforms.Model.StructureComplexElement, { type: 'atomic-het' }, { ref: 'het' })
  77. .apply(StateTransforms.Representation.StructureRepresentation3D,
  78. StructureRepresentation3DHelpers.getDefaultParamsWithTheme(this.plugin,
  79. (style && style.hetGroups && style.hetGroups.kind) || 'ball-and-stick',
  80. (style && style.hetGroups && style.hetGroups.coloring), structure),
  81. { ref: 'het-visual' });
  82. root.apply(StateTransforms.Model.StructureComplexElement, { type: 'water' }, { ref: 'water' })
  83. .apply(StateTransforms.Representation.StructureRepresentation3D,
  84. StructureRepresentation3DHelpers.getDefaultParamsWithTheme(this.plugin,
  85. (style && style.water && style.water.kind) || 'ball-and-stick',
  86. (style && style.water && style.water.coloring), structure, { alpha: 0.51 }),
  87. { ref: 'water-visual' });
  88. return root;
  89. }
  90. private getObj<T extends StateObject>(ref: string): T['data'] {
  91. const state = this.state;
  92. const cell = state.select(ref)[0];
  93. if (!cell || !cell.obj) return void 0;
  94. return (cell.obj as T).data;
  95. }
  96. private async doInfo(checkPreferredAssembly: boolean) {
  97. const model = this.getObj<PluginStateObject.Molecule.Model>('model');
  98. if (!model) return;
  99. const info = await ModelInfo.get(this.plugin, model, checkPreferredAssembly)
  100. this.events.modelInfo.next(info);
  101. return info;
  102. }
  103. private applyState(tree: StateBuilder) {
  104. return PluginCommands.State.Update.dispatch(this.plugin, { state: this.plugin.state.dataState, tree });
  105. }
  106. private loadedParams: LoadParams = { url: '', format: 'cif', assemblyId: '' };
  107. async load({ url, format = 'cif', assemblyId = '', representationStyle }: LoadParams) {
  108. let loadType: 'full' | 'update' = 'full';
  109. const state = this.plugin.state.dataState;
  110. if (this.loadedParams.url !== url || this.loadedParams.format !== format) {
  111. loadType = 'full';
  112. } else if (this.loadedParams.url === url) {
  113. if (state.select('asm').length > 0) loadType = 'update';
  114. }
  115. if (loadType === 'full') {
  116. await PluginCommands.State.RemoveObject.dispatch(this.plugin, { state, ref: state.tree.root.ref });
  117. const modelTree = this.model(this.download(state.build().toRoot(), url), format, assemblyId);
  118. await this.applyState(modelTree);
  119. const info = await this.doInfo(true);
  120. const structureTree = this.structure((assemblyId === 'preferred' && info && info.preferredAssemblyId) || assemblyId);
  121. await this.applyState(structureTree);
  122. } else {
  123. const tree = state.build();
  124. tree.to('asm').update(StateTransforms.Model.StructureAssemblyFromModel, p => ({ ...p, id: assemblyId || 'deposited' }));
  125. await this.applyState(tree);
  126. }
  127. await this.updateStyle(representationStyle);
  128. this.loadedParams = { url, format, assemblyId };
  129. PluginCommands.Camera.Reset.dispatch(this.plugin, { });
  130. }
  131. async updateStyle(style?: RepresentationStyle) {
  132. const tree = this.visual('asm', style);
  133. if (!tree) return;
  134. await PluginCommands.State.Update.dispatch(this.plugin, { state: this.plugin.state.dataState, tree });
  135. }
  136. setBackground(color: number) {
  137. PluginCommands.Canvas3D.SetSettings.dispatch(this.plugin, { settings: { backgroundColor: Color(color) } });
  138. }
  139. toggleSpin() {
  140. const trackball = this.plugin.canvas3d.props.trackball;
  141. const spinning = trackball.spin;
  142. PluginCommands.Canvas3D.SetSettings.dispatch(this.plugin, { settings: { trackball: { ...trackball, spin: !trackball.spin } } });
  143. if (!spinning) PluginCommands.Camera.Reset.dispatch(this.plugin, { });
  144. }
  145. animate = {
  146. modelIndex: {
  147. maxFPS: 8,
  148. onceForward: () => { this.plugin.state.animation.play(AnimateModelIndex, { maxFPS: Math.max(0.5, this.animate.modelIndex.maxFPS | 0), mode: { name: 'once', params: { direction: 'forward' } } }) },
  149. onceBackward: () => { this.plugin.state.animation.play(AnimateModelIndex, { maxFPS: Math.max(0.5, this.animate.modelIndex.maxFPS | 0), mode: { name: 'once', params: { direction: 'backward' } } }) },
  150. palindrome: () => { this.plugin.state.animation.play(AnimateModelIndex, { maxFPS: Math.max(0.5, this.animate.modelIndex.maxFPS | 0), mode: { name: 'palindrome', params: {} } }) },
  151. loop: () => { this.plugin.state.animation.play(AnimateModelIndex, { maxFPS: Math.max(0.5, this.animate.modelIndex.maxFPS | 0), mode: { name: 'loop', params: {} } }) },
  152. stop: () => this.plugin.state.animation.stop()
  153. }
  154. }
  155. coloring = {
  156. evolutionaryConservation: async () => {
  157. await this.updateStyle({ sequence: { kind: 'spacefill' } });
  158. const state = this.state;
  159. // const visuals = state.selectQ(q => q.ofType(PluginStateObject.Molecule.Structure.Representation3D).filter(c => c.transform.transformer === StateTransforms.Representation.StructureRepresentation3D));
  160. const tree = state.build();
  161. const colorTheme = { name: EvolutionaryConservation.Descriptor.name, params: this.plugin.structureRepresentation.themeCtx.colorThemeRegistry.get(EvolutionaryConservation.Descriptor.name).defaultValues };
  162. tree.to('sequence-visual').update(StateTransforms.Representation.StructureRepresentation3D, old => ({ ...old, colorTheme }));
  163. // for (const v of visuals) {
  164. // }
  165. await PluginCommands.State.Update.dispatch(this.plugin, { state, tree });
  166. }
  167. }
  168. snapshot = {
  169. get: () => {
  170. return this.plugin.state.getSnapshot();
  171. },
  172. set: (snapshot: PluginState.Snapshot) => {
  173. return this.plugin.state.setSnapshot(snapshot);
  174. },
  175. download: async (url: string) => {
  176. try {
  177. const data = await this.plugin.runTask(this.plugin.fetch({ url }));
  178. const snapshot = JSON.parse(data);
  179. await this.plugin.state.setSnapshot(snapshot);
  180. } catch (e) {
  181. console.log(e);
  182. }
  183. }
  184. }
  185. }
  186. (window as any).MolStarProteopediaWrapper = MolStarProteopediaWrapper;