behavior.ts 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337
  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. /**
  8. * Copyright (C) 2022, Protein Bioinformatics Research Group, RCNS
  9. *
  10. * Licensed under CC BY-NC 4.0, see LICENSE file for more info.
  11. *
  12. * @author Gabor Tusnady <tusnady.gabor@ttk.hu>
  13. * @author Csongor Gerdan <gerdan.csongor@ttk.hu>
  14. */
  15. import { ParamDefinition as PD } from 'molstar/lib/mol-util/param-definition';
  16. import { StructureRepresentationPresetProvider, PresetStructureRepresentations } from 'molstar/lib/mol-plugin-state/builder/structure/representation-preset';
  17. import { StateObject, StateObjectRef, StateObjectCell, StateTransformer, StateTransform } from 'molstar/lib/mol-state';
  18. import { Task } from 'molstar/lib/mol-task';
  19. import { PluginBehavior } from 'molstar/lib/mol-plugin/behavior';
  20. import { PluginStateObject, PluginStateTransform } from 'molstar/lib/mol-plugin-state/objects';
  21. import { PluginContext } from 'molstar/lib/mol-plugin/context';
  22. import { DefaultQueryRuntimeTable } from 'molstar/lib/mol-script/runtime/query/compiler';
  23. import { StructureSelectionQuery, StructureSelectionCategory } from 'molstar/lib/mol-plugin-state/helpers/structure-selection-query';
  24. import { MolScriptBuilder as MS } from 'molstar/lib/mol-script/language/builder';
  25. import { GenericRepresentationRef } from 'molstar/lib/mol-plugin-state/manager/structure/hierarchy-state';
  26. import { PluginUIContext } from 'molstar/lib/mol-plugin-ui/context';
  27. // TMDET imports
  28. import { MembraneOrientationRepresentationProvider, MembraneOrientationParams, MembraneOrientationRepresentation } from './representation';
  29. import { MembraneOrientationProvider, TmDetDescriptorCache, isTransmembrane as tmSymbol, setMembraneOrientation } from './prop';
  30. import { applyTransformations, createMembraneOrientation } from './transformation';
  31. import { ComponentsType, PDBTMDescriptor, PMS } from './types';
  32. import { registerTmDetSymmetry } from './symmetry';
  33. import { TmDetLabelProvider } from './labeling';
  34. import { TmDetColorThemeProvider, updateSiteColors } from './tmdet-color-theme';
  35. //import { loadInitialSnapshot, rotateCamera, storeCameraSnapshot } from './camera';
  36. import { DebugUtil } from './debug-utils';
  37. const TMDET_MEMB_ORI_REPRESENTATION_TAG = 'tmdet-membrane-orientation-3d';
  38. const TMDET_MEMBRANE_ORIENTATION = 'TMDET Membrane Orientation';
  39. export const TMDET_STRUCTURE_PRESET_ID = 'tmdet-preset-membrane-orientation';
  40. export const TMDETMembraneOrientation = PluginBehavior.create<{ autoAttach: boolean }>({
  41. name: 'tmdet-membrane-orientation-prop',
  42. category: 'custom-props',
  43. display: {
  44. name: TMDET_MEMBRANE_ORIENTATION,
  45. description: 'Data calculated with TMDET algorithm.'
  46. },
  47. ctor: class extends PluginBehavior.Handler<{ autoAttach: boolean }> {
  48. private provider = MembraneOrientationProvider
  49. register(): void {
  50. console.log('TMDET plugin behavior REGISTER');
  51. DefaultQueryRuntimeTable.addCustomProp(this.provider.descriptor);
  52. this.ctx.customStructureProperties.register(this.provider, this.params.autoAttach);
  53. this.ctx.representation.structure.registry.add(MembraneOrientationRepresentationProvider);
  54. this.ctx.query.structure.registry.add(isTransmembrane);
  55. this.ctx.representation.structure.themes.colorThemeRegistry.add(TmDetColorThemeProvider);
  56. this.ctx.managers.lociLabels.addProvider(TmDetLabelProvider);
  57. this.ctx.genericRepresentationControls.set(TMDET_MEMB_ORI_REPRESENTATION_TAG, selection => {
  58. const refs: GenericRepresentationRef[] = [];
  59. selection.structures.forEach(structure => {
  60. const memRepr = structure.genericRepresentations?.filter(r => r.cell.transform.transformer.id === MembraneOrientation3D.id)[0];
  61. if (memRepr) refs.push(memRepr);
  62. });
  63. return [refs, 'Membrane Orientation'];
  64. });
  65. this.ctx.builders.structure.representation.registerPreset(MembraneOrientationPreset);
  66. }
  67. update(p: { autoAttach: boolean }) {
  68. console.log('TMDET plugin behavior UPDATE');
  69. let updated = this.params.autoAttach !== p.autoAttach;
  70. this.params.autoAttach = p.autoAttach;
  71. this.ctx.customStructureProperties.setDefaultAutoAttach(this.provider.descriptor.name, this.params.autoAttach);
  72. return updated;
  73. }
  74. unregister() {
  75. console.log('TMDET plugin behavior UNREGISTER');
  76. DefaultQueryRuntimeTable.removeCustomProp(this.provider.descriptor);
  77. this.ctx.customStructureProperties.unregister(this.provider.descriptor.name);
  78. this.ctx.representation.structure.registry.remove(MembraneOrientationRepresentationProvider);
  79. this.ctx.query.structure.registry.remove(isTransmembrane);
  80. this.ctx.genericRepresentationControls.delete(TMDET_MEMB_ORI_REPRESENTATION_TAG);
  81. this.ctx.builders.structure.representation.unregisterPreset(MembraneOrientationPreset);
  82. this.ctx.representation.structure.themes.colorThemeRegistry.remove(TmDetColorThemeProvider);
  83. this.ctx.managers.lociLabels.removeProvider(TmDetLabelProvider);
  84. }
  85. },
  86. params: () => ({
  87. autoAttach: PD.Boolean(false)
  88. })
  89. });
  90. //
  91. export const isTransmembrane = StructureSelectionQuery('Residues Embedded in Membrane', MS.struct.modifier.union([
  92. MS.struct.modifier.wholeResidues([
  93. MS.struct.modifier.union([
  94. MS.struct.generator.atomGroups({
  95. 'chain-test': MS.core.rel.eq([MS.ammp('objectPrimitive'), 'atomistic']),
  96. 'atom-test': tmSymbol.symbol(),
  97. })
  98. ])
  99. ])
  100. ]), {
  101. description: 'Select residues that are embedded between the membrane layers.',
  102. category: StructureSelectionCategory.Residue,
  103. ensureCustomProperties: (ctx, structure) => {
  104. return MembraneOrientationProvider.attach(ctx, structure);
  105. }
  106. });
  107. //
  108. // //////////////////////////// TMDET VIEWER FUNCTIONS
  109. //
  110. export async function loadWithUNITMPMembraneRepresentation(plugin: PluginUIContext, params: any) {
  111. //storeCameraSnapshot(plugin); // store if it is not stored yet
  112. //loadInitialSnapshot(plugin); // load if there is a stored one
  113. setTimeout(() => { plugin.clear(); }, 100); // clear scene after some delay
  114. updateSiteColors(params.side1);
  115. setTimeout(() => { (async () => {
  116. const pdbtmDescriptor: PDBTMDescriptor = await downloadRegionDescriptor(plugin, params);
  117. pdbtmDescriptor.side1 = params.side1;
  118. TmDetDescriptorCache.add(pdbtmDescriptor);
  119. const membraneOrientation = createMembraneOrientation(pdbtmDescriptor);
  120. setMembraneOrientation(membraneOrientation);
  121. // load structure
  122. await loadStructure(plugin, params, pdbtmDescriptor);
  123. // cartoon, colors etc.
  124. await createStructureRepresentation(plugin, pdbtmDescriptor);
  125. //
  126. // It also resets the camera because the membranes render 1st and the structure might not be fully visible
  127. //
  128. //rotateCamera(plugin);
  129. })(); }, 500);
  130. }
  131. async function downloadRegionDescriptor(plugin: PluginUIContext, params: any): Promise<any> {
  132. // run a fetch task
  133. const downloadResult: string = await plugin.runTask(plugin.fetch({ url: params.regionDescriptorUrl })) as string;
  134. const pdbtmDescriptor: any = JSON.parse(downloadResult);
  135. return pdbtmDescriptor;
  136. }
  137. async function createStructureRepresentation(plugin: PluginUIContext, pdbtmDescriptor: any) {
  138. // get the first structure of the first model
  139. const structure: StateObjectRef<PMS> = plugin.managers.structure.hierarchy.current.models[0].structures[0].cell;
  140. const components = await createStructureComponents(plugin, structure);
  141. applyTransformations(plugin, pdbtmDescriptor);
  142. await buildStructureRepresentation(plugin, pdbtmDescriptor, components);
  143. }
  144. async function createStructureComponents(plugin: PluginUIContext, structure: StateObjectCell<PMS, StateTransform<StateTransformer<StateObject<any, StateObject.Type<any>>, StateObject<any, StateObject.Type<any>>, any>>>) {
  145. return {
  146. polymer: await plugin.builders.structure.tryCreateComponentStatic(structure, 'polymer'),
  147. ligand: await plugin.builders.structure.tryCreateComponentStatic(structure, 'ligand'),
  148. water: await plugin.builders.structure.tryCreateComponentStatic(structure, 'water'),
  149. };
  150. }
  151. async function buildStructureRepresentation(plugin: PluginUIContext, pdbtmDescriptor: PDBTMDescriptor, components: ComponentsType) {
  152. const builder = plugin.builders.structure.representation;
  153. const update = plugin.build();
  154. if (components.polymer) {
  155. builder.buildRepresentation(update, components.polymer, {
  156. type: 'cartoon',
  157. color: TmDetColorThemeProvider.name as any, colorParams: { pdbtmDescriptor }
  158. },
  159. { tag: 'polymer' }
  160. );
  161. }
  162. if (components.ligand)
  163. builder.buildRepresentation(update, components.ligand, { type: 'ball-and-stick' }, { tag: 'ligand' });
  164. if (components.water)
  165. builder.buildRepresentation(update, components.water, { type: 'ball-and-stick', typeParams: { alpha: 0.6 } }, { tag: 'water' });
  166. await update.commit();
  167. }
  168. async function loadStructure(ctx: PluginUIContext, params: any, pdbtmDescriptor: PDBTMDescriptor): Promise<void> {
  169. // replace original symmetry format function
  170. registerTmDetSymmetry(pdbtmDescriptor);
  171. let format: "mmcif"|"pdb" = "mmcif";
  172. // check url to determine file format and compression
  173. const match: string[] = params.structureUrl.match(new RegExp('/(pdb|cif)(.gz)?$/g/'));
  174. DebugUtil.log(`format: ${params.format}`);
  175. if ((params.format != 'mmcif' || params.format != 'pdb') && match != null) {
  176. if (match[0].startsWith('cif')) {
  177. format = 'mmcif';
  178. }
  179. if (match[0].startsWith('pdb')) {
  180. format = 'pdb';
  181. }
  182. } else if (params.format) {
  183. format = params.format;
  184. }
  185. const builders = ctx.builders;
  186. const data = await downloadData(ctx, params, pdbtmDescriptor);
  187. const trajectory = await builders.structure.parseTrajectory(data, format);
  188. // create membrane representation
  189. await builders.structure.hierarchy.applyPreset(
  190. trajectory, 'default', { representationPreset: TMDET_STRUCTURE_PRESET_ID as any });
  191. }
  192. async function downloadData(ctx: PluginUIContext, params: any, pdbtmDescriptor: PDBTMDescriptor) {
  193. const builders = ctx.builders;
  194. let downloadResult = await ctx.runTask(ctx.fetch({ url: params.structureUrl, type: "string" }));
  195. DebugUtil.log('First 50 chars of input data', downloadResult.slice(0, 50));
  196. return await builders.data.rawData({
  197. data: downloadResult,
  198. label: `${pdbtmDescriptor.pdb_id}`,
  199. }); // , { state: { isGhost: true } });
  200. }
  201. //
  202. // //////////////////////////// END OF TMDET VIEWER SECTION
  203. //
  204. type MembraneOrientation3DType = typeof MembraneOrientation3D
  205. const MembraneOrientation3D = PluginStateTransform.BuiltIn({
  206. name: TMDET_MEMB_ORI_REPRESENTATION_TAG,
  207. display: {
  208. name: TMDET_MEMBRANE_ORIENTATION,
  209. description: 'Membrane Orientation planes and rims. Data calculated with TMDET algorithm.'
  210. },
  211. from: PluginStateObject.Molecule.Structure,
  212. to: PluginStateObject.Shape.Representation3D,
  213. params: () => {
  214. return {
  215. ...MembraneOrientationParams,
  216. };
  217. }
  218. })({
  219. canAutoUpdate({ oldParams, newParams }) {
  220. return true;
  221. },
  222. apply({ a, params }, plugin: PluginContext) {
  223. return Task.create(TMDET_MEMBRANE_ORIENTATION, async ctx => {
  224. await MembraneOrientationProvider.attach({ runtime: ctx, assetManager: plugin.managers.asset }, a.data);
  225. const repr = MembraneOrientationRepresentation({ webgl: plugin.canvas3d?.webgl, ...plugin.representation.structure.themes }, () => MembraneOrientationParams);
  226. await repr.createOrUpdate(params, a.data).runInContext(ctx);
  227. return new PluginStateObject.Shape.Representation3D({ repr, sourceData: a.data }, { label: TMDET_MEMBRANE_ORIENTATION });
  228. });
  229. },
  230. update({ a, b, newParams }, plugin: PluginContext) {
  231. return Task.create(TMDET_MEMBRANE_ORIENTATION, async ctx => {
  232. await MembraneOrientationProvider.attach({ runtime: ctx, assetManager: plugin.managers.asset }, a.data);
  233. const props = { ...b.data.repr.props, ...newParams };
  234. await b.data.repr.createOrUpdate(props, a.data).runInContext(ctx);
  235. b.data.sourceData = a.data;
  236. return StateTransformer.UpdateResult.Updated;
  237. });
  238. },
  239. isApplicable(a) {
  240. return MembraneOrientationProvider.isApplicable(a.data);
  241. }
  242. });
  243. export const MembraneOrientationPreset = StructureRepresentationPresetProvider({
  244. id: TMDET_STRUCTURE_PRESET_ID,
  245. display: {
  246. name: TMDET_MEMBRANE_ORIENTATION, group: 'Annotation',
  247. description: 'Shows orientation of membrane layers. Data calculated with TMDET algorithm.' // TODO add ' or obtained via RCSB PDB'
  248. },
  249. isApplicable(a) {
  250. return MembraneOrientationProvider.isApplicable(a.data);
  251. },
  252. params: () => StructureRepresentationPresetProvider.CommonParams,
  253. async apply(ref, params, plugin) {
  254. const structureCell = StateObjectRef.resolveAndCheck(plugin.state.data, ref);
  255. const structure = structureCell?.obj?.data;
  256. if (!structureCell || !structure) return {};
  257. if (!MembraneOrientationProvider.get(structure).value) {
  258. await plugin.runTask(Task.create(TMDET_MEMBRANE_ORIENTATION, async runtime => {
  259. await MembraneOrientationProvider.attach({ runtime, assetManager: plugin.managers.asset }, structure);
  260. }));
  261. }
  262. const membraneOrientation = await tryCreateMembraneOrientation(plugin, structureCell);
  263. const colorTheme = TmDetColorThemeProvider.name as any;
  264. console.log('MembOriPreset apply params:', params);
  265. console.log('MembOriPreset ref:', ref);
  266. const preset = await PresetStructureRepresentations.auto.apply(ref, { ...params, theme: { globalName: colorTheme, focus: { name: colorTheme } } }, plugin);
  267. return { components: preset.components, representations: { ...preset.representations, membraneOrientation } };
  268. }
  269. });
  270. export function tryCreateMembraneOrientation(plugin: PluginContext, structure: StateObjectRef<PMS>, params?: StateTransformer.Params<MembraneOrientation3DType>, initialState?: Partial<StateTransform.State>) {
  271. const state = plugin.state.data;
  272. const membraneOrientation = state.build().to(structure)
  273. .applyOrUpdateTagged(TMDET_MEMB_ORI_REPRESENTATION_TAG, MembraneOrientation3D, params, { state: initialState });
  274. return membraneOrientation.commit({ revertOnError: true });
  275. }