structure.ts 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  1. /**
  2. * Copyright (c) 2018-2019 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 { PluginContext } from 'mol-plugin/context';
  8. import { StateAction, StateBuilder, StateSelection, StateTransformer } from 'mol-state';
  9. import { ParamDefinition as PD } from 'mol-util/param-definition';
  10. import { PluginStateObject } from '../objects';
  11. import { StateTransforms } from '../transforms';
  12. import { Download } from '../transforms/data';
  13. import { StructureRepresentation3DHelpers } from '../transforms/representation';
  14. import { CustomModelProperties } from '../transforms/model';
  15. // TODO: "structure/volume parser provider"
  16. export { DownloadStructure };
  17. type DownloadStructure = typeof DownloadStructure
  18. const DownloadStructure = StateAction.build({
  19. from: PluginStateObject.Root,
  20. display: { name: 'Download Structure', description: 'Load a structure from the provided source and create its default Assembly and visual.' },
  21. params: {
  22. source: PD.MappedStatic('bcif-static', {
  23. 'pdbe-updated': PD.Group({
  24. id: PD.Text('1cbs', { label: 'Id' }),
  25. supportProps: PD.Boolean(false)
  26. }, { isFlat: true }),
  27. 'rcsb': PD.Group({
  28. id: PD.Text('1tqn', { label: 'Id' }),
  29. supportProps: PD.Boolean(false)
  30. }, { isFlat: true }),
  31. 'bcif-static': PD.Group({
  32. id: PD.Text('1tqn', { label: 'Id' }),
  33. supportProps: PD.Boolean(false)
  34. }, { isFlat: true }),
  35. 'url': PD.Group({
  36. url: PD.Text(''),
  37. format: PD.Select('cif', [['cif', 'CIF'], ['pdb', 'PDB']]),
  38. isBinary: PD.Boolean(false),
  39. supportProps: PD.Boolean(false)
  40. }, { isFlat: true })
  41. }, {
  42. options: [
  43. ['pdbe-updated', 'PDBe Updated'],
  44. ['rcsb', 'RCSB'],
  45. ['bcif-static', 'BinaryCIF (static PDBe Updated)'],
  46. ['url', 'URL']
  47. ]
  48. })
  49. }
  50. })(({ params, state }, ctx: PluginContext) => {
  51. const b = state.build();
  52. const src = params.source;
  53. let downloadParams: StateTransformer.Params<Download>;
  54. switch (src.name) {
  55. case 'url':
  56. downloadParams = { url: src.params.url, isBinary: src.params.isBinary };
  57. break;
  58. case 'pdbe-updated':
  59. downloadParams = { url: `https://www.ebi.ac.uk/pdbe/static/entry/${src.params.id.toLowerCase()}_updated.cif`, isBinary: false, label: `PDBe: ${src.params.id}` };
  60. break;
  61. case 'rcsb':
  62. downloadParams = { url: `https://files.rcsb.org/download/${src.params.id.toUpperCase()}.cif`, isBinary: false, label: `RCSB: ${src.params.id}` };
  63. break;
  64. case 'bcif-static':
  65. downloadParams = { url: `https://webchem.ncbr.muni.cz/ModelServer/static/bcif/${src.params.id.toLowerCase()}`, isBinary: true, label: `BinaryCIF: ${src.params.id}` };
  66. break;
  67. default: throw new Error(`${(src as any).name} not supported.`);
  68. }
  69. const data = b.toRoot().apply(StateTransforms.Data.Download, downloadParams, { props: { isGhost: true }});
  70. const traj = createModelTree(data, src.name === 'url' ? src.params.format : 'cif');
  71. return state.updateTree(createStructureTree(ctx, traj, params.source.params.supportProps));
  72. });
  73. export const OpenStructure = StateAction.build({
  74. display: { name: 'Open Structure', description: 'Load a structure from file and create its default Assembly and visual' },
  75. from: PluginStateObject.Root,
  76. params: { file: PD.File({ accept: '.cif,.bcif' }) }
  77. })(({ params, state }, ctx: PluginContext) => {
  78. const b = state.build();
  79. const data = b.toRoot().apply(StateTransforms.Data.ReadFile, { file: params.file, isBinary: /\.bcif$/i.test(params.file.name) });
  80. const traj = createModelTree(data, 'cif');
  81. return state.updateTree(createStructureTree(ctx, traj, false));
  82. });
  83. function createModelTree(b: StateBuilder.To<PluginStateObject.Data.Binary | PluginStateObject.Data.String>, format: 'pdb' | 'cif' = 'cif') {
  84. const parsed = format === 'cif'
  85. ? b.apply(StateTransforms.Data.ParseCif, void 0, { props: { isGhost: true }}).apply(StateTransforms.Model.TrajectoryFromMmCif, void 0, { props: { isGhost: true }})
  86. : b.apply(StateTransforms.Model.TrajectoryFromPDB, void 0, { props: { isGhost: true }});
  87. return parsed.apply(StateTransforms.Model.ModelFromTrajectory, { modelIndex: 0 });
  88. }
  89. function createStructureTree(ctx: PluginContext, b: StateBuilder.To<PluginStateObject.Molecule.Model>, supportProps: boolean) {
  90. let root = b;
  91. if (supportProps) {
  92. root = root.apply(StateTransforms.Model.CustomModelProperties);
  93. }
  94. const structure = root.apply(StateTransforms.Model.StructureAssemblyFromModel);
  95. complexRepresentation(ctx, structure);
  96. return root;
  97. }
  98. function complexRepresentation(ctx: PluginContext, root: StateBuilder.To<PluginStateObject.Molecule.Structure>) {
  99. root.apply(StateTransforms.Model.StructureComplexElement, { type: 'atomic-sequence' })
  100. .apply(StateTransforms.Representation.StructureRepresentation3D,
  101. StructureRepresentation3DHelpers.getDefaultParamsStatic(ctx, 'cartoon'));
  102. root.apply(StateTransforms.Model.StructureComplexElement, { type: 'atomic-het' })
  103. .apply(StateTransforms.Representation.StructureRepresentation3D,
  104. StructureRepresentation3DHelpers.getDefaultParamsStatic(ctx, 'ball-and-stick'));
  105. root.apply(StateTransforms.Model.StructureComplexElement, { type: 'water' })
  106. .apply(StateTransforms.Representation.StructureRepresentation3D,
  107. StructureRepresentation3DHelpers.getDefaultParamsStatic(ctx, 'ball-and-stick', { alpha: 0.51 }));
  108. root.apply(StateTransforms.Model.StructureComplexElement, { type: 'spheres' })
  109. .apply(StateTransforms.Representation.StructureRepresentation3D,
  110. StructureRepresentation3DHelpers.getDefaultParamsStatic(ctx, 'spacefill'));
  111. }
  112. export const CreateComplexRepresentation = StateAction.build({
  113. display: { name: 'Create Complex', description: 'Split the structure into Sequence/Water/Ligands/... ' },
  114. from: PluginStateObject.Molecule.Structure
  115. })(({ ref, state }, ctx: PluginContext) => {
  116. const root = state.build().to(ref);
  117. complexRepresentation(ctx, root);
  118. return state.updateTree(root);
  119. });
  120. export const UpdateTrajectory = StateAction.build({
  121. display: { name: 'Update Trajectory' },
  122. params: {
  123. action: PD.Select<'advance' | 'reset'>('advance', [['advance', 'Advance'], ['reset', 'Reset']]),
  124. by: PD.makeOptional(PD.Numeric(1, { min: -1, max: 1, step: 1 }))
  125. }
  126. })(({ params, state }) => {
  127. const models = state.selectQ(q => q.rootsOfType(PluginStateObject.Molecule.Model)
  128. .filter(c => c.transform.transformer === StateTransforms.Model.ModelFromTrajectory));
  129. const update = state.build();
  130. if (params.action === 'reset') {
  131. for (const m of models) {
  132. update.to(m.transform.ref).update(StateTransforms.Model.ModelFromTrajectory,
  133. () => ({ modelIndex: 0 }));
  134. }
  135. } else {
  136. for (const m of models) {
  137. const parent = StateSelection.findAncestorOfType(state.tree, state.cells, m.transform.ref, [PluginStateObject.Molecule.Trajectory]);
  138. if (!parent || !parent.obj) continue;
  139. const traj = parent.obj as PluginStateObject.Molecule.Trajectory;
  140. update.to(m.transform.ref).update(StateTransforms.Model.ModelFromTrajectory,
  141. old => {
  142. let modelIndex = (old.modelIndex + params.by!) % traj.data.length;
  143. if (modelIndex < 0) modelIndex += traj.data.length;
  144. return { modelIndex };
  145. });
  146. }
  147. }
  148. return state.updateTree(update);
  149. });
  150. export const EnableModelCustomProps = StateAction.build({
  151. display: { name: 'Custom Properties', description: 'Enable the addition of custom properties to the model.' },
  152. from: PluginStateObject.Molecule.Model,
  153. params(a, ctx: PluginContext) {
  154. if (!a) return { properties: PD.MultiSelect([], [], { description: 'A list of property descriptor ids.' }) };
  155. return { properties: ctx.customModelProperties.getSelect(a.data) };
  156. },
  157. isApplicable(a, t, ctx: PluginContext) {
  158. return t.transformer !== CustomModelProperties;
  159. }
  160. })(({ ref, params, state }, ctx: PluginContext) => {
  161. const root = state.build().to(ref).insert(CustomModelProperties, params);
  162. return state.updateTree(root);
  163. });