index.ts 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  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/commands';
  10. import { StateTransforms } from '../../mol-plugin-state/transforms';
  11. import { Color } from '../../mol-util/color';
  12. import { PluginStateObject as PSO, PluginStateObject } from '../../mol-plugin-state/objects';
  13. import { AnimateModelIndex } from '../../mol-plugin-state/animation/built-in';
  14. import { StateBuilder, StateTransform } from '../../mol-state';
  15. import { StripedResidues } from './coloring';
  16. import { StaticSuperpositionTestData, buildStaticSuperposition, dynamicSuperpositionTest } from './superposition';
  17. import { PDBeStructureQualityReport } from '../../mol-plugin/behavior/dynamic/custom-props';
  18. import { CustomToastMessage } from './controls';
  19. import { EmptyLoci } from '../../mol-model/loci';
  20. import { StructureSelection } from '../../mol-model/structure';
  21. import { Script } from '../../mol-script/script';
  22. import { createStructureRepresentationParams } from '../../mol-plugin-state/helpers/structure-representation-params';
  23. require('mol-plugin-ui/skin/light.scss')
  24. type SupportedFormats = 'cif' | 'pdb'
  25. type LoadParams = { url: string, format?: SupportedFormats, assemblyId?: string }
  26. class BasicWrapper {
  27. plugin: PluginContext;
  28. init(target: string | HTMLElement) {
  29. this.plugin = createPlugin(typeof target === 'string' ? document.getElementById(target)! : target, {
  30. ...DefaultPluginSpec,
  31. layout: {
  32. initial: {
  33. isExpanded: false,
  34. showControls: false
  35. },
  36. controls: {
  37. // left: 'none',
  38. // right: BasicWrapperControls
  39. }
  40. },
  41. components: {
  42. remoteState: 'none'
  43. }
  44. });
  45. this.plugin.structureRepresentation.themeCtx.colorThemeRegistry.add(StripedResidues.colorThemeProvider!);
  46. this.plugin.managers.lociLabels.addProvider(StripedResidues.labelProvider!);
  47. this.plugin.customModelProperties.register(StripedResidues.propertyProvider, true);
  48. }
  49. private download(b: StateBuilder.To<PSO.Root>, url: string) {
  50. return b.apply(StateTransforms.Data.Download, { url, isBinary: false })
  51. }
  52. private parse(b: StateBuilder.To<PSO.Data.Binary | PSO.Data.String>, format: SupportedFormats, assemblyId: string) {
  53. const parsed = format === 'cif'
  54. ? b.apply(StateTransforms.Data.ParseCif).apply(StateTransforms.Model.TrajectoryFromMmCif)
  55. : b.apply(StateTransforms.Model.TrajectoryFromPDB);
  56. const props = {
  57. type: {
  58. name: 'assembly' as const,
  59. params: { id: assemblyId || 'deposited' }
  60. }
  61. }
  62. return parsed
  63. .apply(StateTransforms.Model.ModelFromTrajectory, { modelIndex: 0 })
  64. .apply(StateTransforms.Model.CustomModelProperties, { autoAttach: [StripedResidues.propertyProvider.descriptor.name], properties: {} }, { ref: 'props', state: { isGhost: false } })
  65. .apply(StateTransforms.Model.StructureFromModel, props, { ref: 'asm' });
  66. }
  67. private visual(visualRoot: StateBuilder.To<PSO.Molecule.Structure>) {
  68. visualRoot.apply(StateTransforms.Model.StructureComplexElement, { type: 'atomic-sequence' }, { ref: 'seq' })
  69. .apply(StateTransforms.Representation.StructureRepresentation3D,
  70. createStructureRepresentationParams(this.plugin, void 0, { type: 'cartoon' }), { ref: 'seq-visual' });
  71. visualRoot.apply(StateTransforms.Model.StructureComplexElement, { type: 'atomic-het' })
  72. .apply(StateTransforms.Representation.StructureRepresentation3D,
  73. createStructureRepresentationParams(this.plugin, void 0, { type: 'ball-and-stick' }), { ref: 'het-visual' });
  74. visualRoot.apply(StateTransforms.Model.StructureComplexElement, { type: 'water' })
  75. .apply(StateTransforms.Representation.StructureRepresentation3D,
  76. createStructureRepresentationParams(this.plugin, void 0, { type: 'ball-and-stick', typeParams: { alpha: 0.51 } }), { ref: 'water-visual' });
  77. visualRoot.apply(StateTransforms.Model.StructureComplexElement, { type: 'spheres' })
  78. .apply(StateTransforms.Representation.StructureRepresentation3D,
  79. createStructureRepresentationParams(this.plugin, void 0, { type: 'spacefill' }), { ref: 'ihm-visual' });
  80. return visualRoot;
  81. }
  82. private loadedParams: LoadParams = { url: '', format: 'cif', assemblyId: '' };
  83. async load({ url, format = 'cif', assemblyId = '' }: LoadParams) {
  84. let loadType: 'full' | 'update' = 'full';
  85. const state = this.plugin.state.dataState;
  86. if (this.loadedParams.url !== url || this.loadedParams.format !== format) {
  87. loadType = 'full';
  88. } else if (this.loadedParams.url === url) {
  89. if (state.select('asm').length > 0) loadType = 'update';
  90. }
  91. let tree: StateBuilder.Root;
  92. if (loadType === 'full') {
  93. await PluginCommands.State.RemoveObject(this.plugin, { state, ref: state.tree.root.ref });
  94. tree = state.build();
  95. this.visual(this.parse(this.download(tree.toRoot(), url), format, assemblyId));
  96. } else {
  97. const props = {
  98. type: {
  99. name: 'assembly' as const,
  100. params: { id: assemblyId || 'deposited' }
  101. }
  102. }
  103. tree = state.build();
  104. tree.to('asm').update(StateTransforms.Model.StructureFromModel, p => ({ ...p, ...props }));
  105. }
  106. await PluginCommands.State.Update(this.plugin, { state: this.plugin.state.dataState, tree });
  107. this.loadedParams = { url, format, assemblyId };
  108. PluginCommands.Camera.Reset(this.plugin, { });
  109. }
  110. setBackground(color: number) {
  111. const renderer = this.plugin.canvas3d!.props.renderer;
  112. PluginCommands.Canvas3D.SetSettings(this.plugin, { settings: { renderer: { ...renderer, backgroundColor: Color(color) } } });
  113. }
  114. toggleSpin() {
  115. if (!this.plugin.canvas3d) return;
  116. const trackball = this.plugin.canvas3d.props.trackball;
  117. const spinning = trackball.spin;
  118. PluginCommands.Canvas3D.SetSettings(this.plugin, { settings: { trackball: { ...trackball, spin: !trackball.spin } } });
  119. if (!spinning) PluginCommands.Camera.Reset(this.plugin, { });
  120. }
  121. animate = {
  122. modelIndex: {
  123. maxFPS: 8,
  124. onceForward: () => { this.plugin.state.animation.play(AnimateModelIndex, { maxFPS: Math.max(0.5, this.animate.modelIndex.maxFPS | 0), mode: { name: 'once', params: { direction: 'forward' } } }) },
  125. onceBackward: () => { this.plugin.state.animation.play(AnimateModelIndex, { maxFPS: Math.max(0.5, this.animate.modelIndex.maxFPS | 0), mode: { name: 'once', params: { direction: 'backward' } } }) },
  126. palindrome: () => { this.plugin.state.animation.play(AnimateModelIndex, { maxFPS: Math.max(0.5, this.animate.modelIndex.maxFPS | 0), mode: { name: 'palindrome', params: {} } }) },
  127. loop: () => { this.plugin.state.animation.play(AnimateModelIndex, { maxFPS: Math.max(0.5, this.animate.modelIndex.maxFPS | 0), mode: { name: 'loop', params: {} } }) },
  128. stop: () => this.plugin.state.animation.stop()
  129. }
  130. }
  131. coloring = {
  132. applyStripes: async () => {
  133. const state = this.plugin.state.dataState;
  134. const visuals = state.selectQ(q => q.ofTransformer(StateTransforms.Representation.StructureRepresentation3D));
  135. const tree = state.build();
  136. const colorTheme = { name: StripedResidues.propertyProvider.descriptor.name, params: this.plugin.structureRepresentation.themeCtx.colorThemeRegistry.get(StripedResidues.propertyProvider.descriptor.name).defaultValues };
  137. for (const v of visuals) {
  138. tree.to(v).update(old => ({ ...old, colorTheme }));
  139. }
  140. await PluginCommands.State.Update(this.plugin, { state, tree });
  141. }
  142. }
  143. interactivity = {
  144. highlightOn: () => {
  145. const seq_id = 7;
  146. const data = (this.plugin.state.dataState.select('asm')[0].obj as PluginStateObject.Molecule.Structure).data;
  147. const sel = Script.getStructureSelection(Q => Q.struct.generator.atomGroups({
  148. 'residue-test': Q.core.rel.eq([Q.struct.atomProperty.macromolecular.label_seq_id(), seq_id]),
  149. 'group-by': Q.struct.atomProperty.macromolecular.residueKey()
  150. }), data);
  151. const loci = StructureSelection.toLociWithSourceUnits(sel);
  152. this.plugin.managers.interactivity.lociHighlights.highlightOnly({ loci });
  153. },
  154. clearHighlight: () => {
  155. this.plugin.managers.interactivity.lociHighlights.highlightOnly({ loci: EmptyLoci });
  156. }
  157. }
  158. tests = {
  159. staticSuperposition: async () => {
  160. const state = this.plugin.state.dataState;
  161. const tree = buildStaticSuperposition(this.plugin, StaticSuperpositionTestData);
  162. await PluginCommands.State.RemoveObject(this.plugin, { state, ref: StateTransform.RootRef });
  163. await PluginCommands.State.Update(this.plugin, { state, tree });
  164. },
  165. dynamicSuperposition: async () => {
  166. await PluginCommands.State.RemoveObject(this.plugin, { state: this.plugin.state.dataState, ref: StateTransform.RootRef });
  167. await dynamicSuperpositionTest(this.plugin, ['1tqn', '2hhb', '4hhb'], 'HEM');
  168. },
  169. toggleValidationTooltip: async () => {
  170. const state = this.plugin.state.behaviorState;
  171. const tree = state.build().to(PDBeStructureQualityReport.id).update(PDBeStructureQualityReport, p => ({ ...p, showTooltip: !p.showTooltip }));
  172. await PluginCommands.State.Update(this.plugin, { state, tree });
  173. },
  174. showToasts: () => {
  175. PluginCommands.Toast.Show(this.plugin, {
  176. title: 'Toast 1',
  177. message: 'This is an example text, timeout 3s',
  178. key: 'toast-1',
  179. timeoutMs: 3000
  180. });
  181. PluginCommands.Toast.Show(this.plugin, {
  182. title: 'Toast 2',
  183. message: CustomToastMessage,
  184. key: 'toast-2'
  185. });
  186. },
  187. hideToasts: () => {
  188. PluginCommands.Toast.Hide(this.plugin, { key: 'toast-1' });
  189. PluginCommands.Toast.Hide(this.plugin, { key: 'toast-2' });
  190. }
  191. }
  192. }
  193. (window as any).BasicMolStarWrapper = new BasicWrapper();