structure.ts 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232
  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, State } 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. import { DataFormatProvider } from './data-format';
  16. import { FileInfo } from 'mol-util/file-info';
  17. import { Task } from 'mol-task';
  18. export const MmcifProvider: DataFormatProvider<any> = {
  19. label: 'mmCIF',
  20. description: 'mmCIF',
  21. stringExtensions: ['cif', 'mmcif', 'mcif'],
  22. binaryExtensions: ['bcif'],
  23. isApplicable: (info: FileInfo, data: Uint8Array | string) => {
  24. return info.ext === 'cif' || info.ext === 'mmcif' || info.ext === 'mcif' || info.ext === 'bcif'
  25. },
  26. getDefaultBuilder: (ctx: PluginContext, data: StateBuilder.To<PluginStateObject.Data.Binary | PluginStateObject.Data.String>, state: State) => {
  27. return Task.create('mmCIF default builder', async taskCtx => {
  28. const traj = createModelTree(data, 'cif');
  29. await state.updateTree(createStructureTree(ctx, traj, false)).runInContext(taskCtx)
  30. })
  31. }
  32. }
  33. export const PdbProvider: DataFormatProvider<any> = {
  34. label: 'PDB',
  35. description: 'PDB',
  36. stringExtensions: ['pdb', 'ent'],
  37. binaryExtensions: [],
  38. isApplicable: (info: FileInfo, data: string) => {
  39. return info.ext === 'pdb' || info.ext === 'ent'
  40. },
  41. getDefaultBuilder: (ctx: PluginContext, data: StateBuilder.To<PluginStateObject.Data.String>, state: State) => {
  42. return Task.create('PDB default builder', async taskCtx => {
  43. const traj = createModelTree(data, 'pdb');
  44. await state.updateTree(createStructureTree(ctx, traj, false)).runInContext(taskCtx)
  45. })
  46. }
  47. }
  48. export const GroProvider: DataFormatProvider<any> = {
  49. label: 'GRO',
  50. description: 'GRO',
  51. stringExtensions: ['gro'],
  52. binaryExtensions: [],
  53. isApplicable: (info: FileInfo, data: string) => {
  54. return info.ext === 'gro'
  55. },
  56. getDefaultBuilder: (ctx: PluginContext, data: StateBuilder.To<PluginStateObject.Data.String>, state: State) => {
  57. return Task.create('GRO default builder', async taskCtx => {
  58. const traj = createModelTree(data, 'gro');
  59. await state.updateTree(createStructureTree(ctx, traj, false)).runInContext(taskCtx)
  60. })
  61. }
  62. }
  63. //
  64. export { DownloadStructure };
  65. type DownloadStructure = typeof DownloadStructure
  66. const DownloadStructure = StateAction.build({
  67. from: PluginStateObject.Root,
  68. display: { name: 'Download Structure', description: 'Load a structure from the provided source and create its default Assembly and visual.' },
  69. params: {
  70. source: PD.MappedStatic('bcif-static', {
  71. 'pdbe-updated': PD.Group({
  72. id: PD.Text('1cbs', { label: 'Id' }),
  73. supportProps: PD.Boolean(false)
  74. }, { isFlat: true }),
  75. 'rcsb': PD.Group({
  76. id: PD.Text('1tqn', { label: 'Id' }),
  77. supportProps: PD.Boolean(false)
  78. }, { isFlat: true }),
  79. 'bcif-static': PD.Group({
  80. id: PD.Text('1tqn', { label: 'Id' }),
  81. supportProps: PD.Boolean(false)
  82. }, { isFlat: true }),
  83. 'url': PD.Group({
  84. url: PD.Text(''),
  85. format: PD.Select('cif', [['cif', 'CIF'], ['pdb', 'PDB']]),
  86. isBinary: PD.Boolean(false),
  87. supportProps: PD.Boolean(false)
  88. }, { isFlat: true })
  89. }, {
  90. options: [
  91. ['pdbe-updated', 'PDBe Updated'],
  92. ['rcsb', 'RCSB'],
  93. ['bcif-static', 'BinaryCIF (static PDBe Updated)'],
  94. ['url', 'URL']
  95. ]
  96. })
  97. }
  98. })(({ params, state }, ctx: PluginContext) => {
  99. const b = state.build();
  100. const src = params.source;
  101. let downloadParams: StateTransformer.Params<Download>;
  102. switch (src.name) {
  103. case 'url':
  104. downloadParams = { url: src.params.url, isBinary: src.params.isBinary };
  105. break;
  106. case 'pdbe-updated':
  107. downloadParams = { url: `https://www.ebi.ac.uk/pdbe/static/entry/${src.params.id.toLowerCase()}_updated.cif`, isBinary: false, label: `PDBe: ${src.params.id}` };
  108. break;
  109. case 'rcsb':
  110. downloadParams = { url: `https://files.rcsb.org/download/${src.params.id.toUpperCase()}.cif`, isBinary: false, label: `RCSB: ${src.params.id}` };
  111. break;
  112. case 'bcif-static':
  113. downloadParams = { url: `https://webchem.ncbr.muni.cz/ModelServer/static/bcif/${src.params.id.toLowerCase()}`, isBinary: true, label: `BinaryCIF: ${src.params.id}` };
  114. break;
  115. default: throw new Error(`${(src as any).name} not supported.`);
  116. }
  117. const data = b.toRoot().apply(StateTransforms.Data.Download, downloadParams, { props: { isGhost: true }});
  118. const traj = createModelTree(data, src.name === 'url' ? src.params.format : 'cif');
  119. return state.updateTree(createStructureTree(ctx, traj, params.source.params.supportProps));
  120. });
  121. function createModelTree(b: StateBuilder.To<PluginStateObject.Data.Binary | PluginStateObject.Data.String>, format: 'pdb' | 'cif' | 'gro' = 'cif') {
  122. let parsed: StateBuilder.To<PluginStateObject.Molecule.Trajectory>
  123. switch (format) {
  124. case 'cif':
  125. parsed = b.apply(StateTransforms.Data.ParseCif, void 0, { props: { isGhost: true }})
  126. .apply(StateTransforms.Model.TrajectoryFromMmCif, void 0, { props: { isGhost: true }})
  127. break
  128. case 'pdb':
  129. parsed = b.apply(StateTransforms.Model.TrajectoryFromPDB, void 0, { props: { isGhost: true }});
  130. break
  131. case 'gro':
  132. parsed = b.apply(StateTransforms.Model.TrajectoryFromGRO, void 0, { props: { isGhost: true }});
  133. break
  134. default:
  135. throw new Error('unsupported format')
  136. }
  137. return parsed.apply(StateTransforms.Model.ModelFromTrajectory, { modelIndex: 0 });
  138. }
  139. function createStructureTree(ctx: PluginContext, b: StateBuilder.To<PluginStateObject.Molecule.Model>, supportProps: boolean) {
  140. let root = b;
  141. if (supportProps) {
  142. root = root.apply(StateTransforms.Model.CustomModelProperties);
  143. }
  144. const structure = root.apply(StateTransforms.Model.StructureAssemblyFromModel);
  145. complexRepresentation(ctx, structure);
  146. return root;
  147. }
  148. function complexRepresentation(ctx: PluginContext, root: StateBuilder.To<PluginStateObject.Molecule.Structure>) {
  149. root.apply(StateTransforms.Model.StructureComplexElement, { type: 'atomic-sequence' })
  150. .apply(StateTransforms.Representation.StructureRepresentation3D,
  151. StructureRepresentation3DHelpers.getDefaultParamsStatic(ctx, 'cartoon'));
  152. root.apply(StateTransforms.Model.StructureComplexElement, { type: 'atomic-het' })
  153. .apply(StateTransforms.Representation.StructureRepresentation3D,
  154. StructureRepresentation3DHelpers.getDefaultParamsStatic(ctx, 'ball-and-stick'));
  155. root.apply(StateTransforms.Model.StructureComplexElement, { type: 'water' })
  156. .apply(StateTransforms.Representation.StructureRepresentation3D,
  157. StructureRepresentation3DHelpers.getDefaultParamsStatic(ctx, 'ball-and-stick', { alpha: 0.51 }));
  158. root.apply(StateTransforms.Model.StructureComplexElement, { type: 'spheres' })
  159. .apply(StateTransforms.Representation.StructureRepresentation3D,
  160. StructureRepresentation3DHelpers.getDefaultParamsStatic(ctx, 'spacefill'));
  161. }
  162. export const CreateComplexRepresentation = StateAction.build({
  163. display: { name: 'Create Complex', description: 'Split the structure into Sequence/Water/Ligands/... ' },
  164. from: PluginStateObject.Molecule.Structure
  165. })(({ ref, state }, ctx: PluginContext) => {
  166. const root = state.build().to(ref);
  167. complexRepresentation(ctx, root);
  168. return state.updateTree(root);
  169. });
  170. export const UpdateTrajectory = StateAction.build({
  171. display: { name: 'Update Trajectory' },
  172. params: {
  173. action: PD.Select<'advance' | 'reset'>('advance', [['advance', 'Advance'], ['reset', 'Reset']]),
  174. by: PD.makeOptional(PD.Numeric(1, { min: -1, max: 1, step: 1 }))
  175. }
  176. })(({ params, state }) => {
  177. const models = state.selectQ(q => q.rootsOfType(PluginStateObject.Molecule.Model)
  178. .filter(c => c.transform.transformer === StateTransforms.Model.ModelFromTrajectory));
  179. const update = state.build();
  180. if (params.action === 'reset') {
  181. for (const m of models) {
  182. update.to(m.transform.ref).update(StateTransforms.Model.ModelFromTrajectory,
  183. () => ({ modelIndex: 0 }));
  184. }
  185. } else {
  186. for (const m of models) {
  187. const parent = StateSelection.findAncestorOfType(state.tree, state.cells, m.transform.ref, [PluginStateObject.Molecule.Trajectory]);
  188. if (!parent || !parent.obj) continue;
  189. const traj = parent.obj as PluginStateObject.Molecule.Trajectory;
  190. update.to(m.transform.ref).update(StateTransforms.Model.ModelFromTrajectory,
  191. old => {
  192. let modelIndex = (old.modelIndex + params.by!) % traj.data.length;
  193. if (modelIndex < 0) modelIndex += traj.data.length;
  194. return { modelIndex };
  195. });
  196. }
  197. }
  198. return state.updateTree(update);
  199. });
  200. export const EnableModelCustomProps = StateAction.build({
  201. display: { name: 'Custom Properties', description: 'Enable the addition of custom properties to the model.' },
  202. from: PluginStateObject.Molecule.Model,
  203. params(a, ctx: PluginContext) {
  204. if (!a) return { properties: PD.MultiSelect([], [], { description: 'A list of property descriptor ids.' }) };
  205. return { properties: ctx.customModelProperties.getSelect(a.data) };
  206. },
  207. isApplicable(a, t, ctx: PluginContext) {
  208. return t.transformer !== CustomModelProperties;
  209. }
  210. })(({ ref, params, state }, ctx: PluginContext) => {
  211. const root = state.build().to(ref).insert(CustomModelProperties, params);
  212. return state.updateTree(root);
  213. });