model.ts 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214
  1. /**
  2. * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
  3. *
  4. * @author David Sehnal <david.sehnal@gmail.com>
  5. */
  6. import { PluginStateTransform } from '../objects';
  7. import { PluginStateObject as SO } from '../objects';
  8. import { Task, RuntimeContext } from 'mol-task';
  9. import { Model, Format, Structure, ModelSymmetry, StructureSymmetry, QueryContext, StructureSelection as Sel, StructureQuery, Queries } from 'mol-model/structure';
  10. import { ParamDefinition as PD } from 'mol-util/param-definition';
  11. import Expression from 'mol-script/language/expression';
  12. import { compile } from 'mol-script/runtime/query/compiler';
  13. import { MolScriptBuilder } from 'mol-script/language/builder';
  14. import { StateObject } from 'mol-state';
  15. import { PluginContext } from 'mol-plugin/context';
  16. import { stringToWords } from 'mol-util/string';
  17. import { shapeFromPly } from 'mol-model/shape/formarts/ply/plyData_to_shape';
  18. export { TrajectoryFromMmCif }
  19. type TrajectoryFromMmCif = typeof TrajectoryFromMmCif
  20. const TrajectoryFromMmCif = PluginStateTransform.BuiltIn({
  21. name: 'trajectory-from-mmcif',
  22. display: { name: 'Trajectory from mmCIF', description: 'Identify and create all separate models in the specified CIF data block' },
  23. from: SO.Format.Cif,
  24. to: SO.Molecule.Trajectory,
  25. params(a) {
  26. const { blocks } = a.data;
  27. return {
  28. blockHeader: PD.makeOptional(PD.Select(blocks[0] && blocks[0].header, blocks.map(b => [b.header, b.header] as [string, string]), { description: 'Header of the block to parse' }))
  29. };
  30. }
  31. })({
  32. isApplicable: a => a.data.blocks.length > 0,
  33. apply({ a, params }) {
  34. return Task.create('Parse mmCIF', async ctx => {
  35. const header = params.blockHeader || a.data.blocks[0].header;
  36. const block = a.data.blocks.find(b => b.header === header);
  37. if (!block) throw new Error(`Data block '${[header]}' not found.`);
  38. const models = await Model.create(Format.mmCIF(block)).runInContext(ctx);
  39. if (models.length === 0) throw new Error('No models found.');
  40. const label = { label: models[0].label, description: `${models.length} model${models.length === 1 ? '' : 's'}` };
  41. return new SO.Molecule.Trajectory(models, label);
  42. });
  43. }
  44. });
  45. export { ModelFromTrajectory }
  46. const plus1 = (v: number) => v + 1, minus1 = (v: number) => v - 1;
  47. type ModelFromTrajectory = typeof ModelFromTrajectory
  48. const ModelFromTrajectory = PluginStateTransform.BuiltIn({
  49. name: 'model-from-trajectory',
  50. display: { name: 'Model from Trajectory', description: 'Create a molecular structure from the specified model.' },
  51. from: SO.Molecule.Trajectory,
  52. to: SO.Molecule.Model,
  53. params: a => ({ modelIndex: PD.Converted(plus1, minus1, PD.Numeric(1, { min: 1, max: a.data.length, step: 1 }, { description: 'Model Index' })) })
  54. })({
  55. isApplicable: a => a.data.length > 0,
  56. apply({ a, params }) {
  57. if (params.modelIndex < 0 || params.modelIndex >= a.data.length) throw new Error(`Invalid modelIndex ${params.modelIndex}`);
  58. const model = a.data[params.modelIndex];
  59. const label = { label: `Model ${model.modelNum}` };
  60. return new SO.Molecule.Model(model, label);
  61. }
  62. });
  63. export { StructureFromModel }
  64. type StructureFromModel = typeof StructureFromModel
  65. const StructureFromModel = PluginStateTransform.BuiltIn({
  66. name: 'structure-from-model',
  67. display: { name: 'Structure from Model', description: 'Create a molecular structure from the specified model.' },
  68. from: SO.Molecule.Model,
  69. to: SO.Molecule.Structure
  70. })({
  71. apply({ a }) {
  72. let s = Structure.ofModel(a.data);
  73. const label = { label: a.data.label, description: s.elementCount === 1 ? '1 element' : `${s.elementCount} elements` };
  74. return new SO.Molecule.Structure(s, label);
  75. }
  76. });
  77. function structureDesc(s: Structure) {
  78. return s.elementCount === 1 ? '1 element' : `${s.elementCount} elements`;
  79. }
  80. export { StructureAssemblyFromModel }
  81. type StructureAssemblyFromModel = typeof StructureAssemblyFromModel
  82. const StructureAssemblyFromModel = PluginStateTransform.BuiltIn({
  83. name: 'structure-assembly-from-model',
  84. display: { name: 'Structure Assembly', description: 'Create a molecular structure assembly.' },
  85. from: SO.Molecule.Model,
  86. to: SO.Molecule.Structure,
  87. params(a) {
  88. const model = a.data;
  89. const ids = model.symmetry.assemblies.map(a => [a.id, `${a.id}: ${stringToWords(a.details)}`] as [string, string]);
  90. if (!ids.length) ids.push(['deposited', 'Deposited'])
  91. return { id: PD.Select(ids[0][0], ids, { label: 'Asm Id', description: 'Assembly Id' }) };
  92. }
  93. })({
  94. apply({ a, params }, plugin: PluginContext) {
  95. return Task.create('Build Assembly', async ctx => {
  96. const model = a.data;
  97. const id = params.id;
  98. const asm = ModelSymmetry.findAssembly(model, id);
  99. if (id !== 'deposited' && !asm) throw new Error(`Assembly '${id}' not found`);
  100. const base = Structure.ofModel(model);
  101. if (!asm) {
  102. plugin.log.warn(`Model '${a.label}' has no assembly, returning deposited structure.`);
  103. const label = { label: a.data.label, description: structureDesc(base) };
  104. return new SO.Molecule.Structure(base, label);
  105. }
  106. const s = await StructureSymmetry.buildAssembly(base, id!).runInContext(ctx);
  107. const label = { label: `Assembly ${id}`, description: structureDesc(s) };
  108. return new SO.Molecule.Structure(s, label);
  109. })
  110. }
  111. });
  112. export { StructureSelection }
  113. type StructureSelection = typeof StructureSelection
  114. const StructureSelection = PluginStateTransform.BuiltIn({
  115. name: 'structure-selection',
  116. display: { name: 'Structure Selection', description: 'Create a molecular structure from the specified model.' },
  117. from: SO.Molecule.Structure,
  118. to: SO.Molecule.Structure,
  119. params: {
  120. query: PD.Value<Expression>(MolScriptBuilder.struct.generator.all, { isHidden: true }),
  121. label: PD.makeOptional(PD.Text('', { isHidden: true }))
  122. }
  123. })({
  124. apply({ a, params }) {
  125. // TODO: use cache, add "update"
  126. const compiled = compile<Sel>(params.query);
  127. const result = compiled(new QueryContext(a.data));
  128. const s = Sel.unionStructure(result);
  129. const label = { label: `${params.label || 'Selection'}`, description: structureDesc(s) };
  130. return new SO.Molecule.Structure(s, label);
  131. }
  132. });
  133. export { StructureComplexElement }
  134. namespace StructureComplexElement { export type Types = 'atomic-sequence' | 'water' | 'atomic-het' | 'spheres' }
  135. type StructureComplexElement = typeof StructureComplexElement
  136. const StructureComplexElement = PluginStateTransform.BuiltIn({
  137. name: 'structure-complex-element',
  138. display: { name: 'Complex Element', description: 'Create a molecular structure from the specified model.' },
  139. from: SO.Molecule.Structure,
  140. to: SO.Molecule.Structure,
  141. params: { type: PD.Text<StructureComplexElement.Types>('atomic-sequence', { isHidden: true }) }
  142. })({
  143. apply({ a, params }) {
  144. // TODO: update function.
  145. let query: StructureQuery, label: string;
  146. switch (params.type) {
  147. case 'atomic-sequence': query = Queries.internal.atomicSequence(); label = 'Sequence'; break;
  148. case 'water': query = Queries.internal.water(); label = 'Water'; break;
  149. case 'atomic-het': query = Queries.internal.atomicHet(); label = 'HET Groups/Ligands'; break;
  150. case 'spheres': query = Queries.internal.spheres(); label = 'Coarse Spheres'; break;
  151. default: throw new Error(`${params.type} is a not valid complex element.`);
  152. }
  153. const result = query(new QueryContext(a.data));
  154. const s = Sel.unionStructure(result);
  155. if (s.elementCount === 0) return StateObject.Null;
  156. return new SO.Molecule.Structure(s, { label, description: structureDesc(s) });
  157. }
  158. });
  159. export { CustomModelProperties }
  160. type CustomModelProperties = typeof CustomModelProperties
  161. const CustomModelProperties = PluginStateTransform.BuiltIn({
  162. name: 'custom-model-properties',
  163. display: { name: 'Custom Model Properties' },
  164. from: SO.Molecule.Model,
  165. to: SO.Molecule.Model,
  166. params: (a, ctx: PluginContext) => ({ properties: ctx.customModelProperties.getSelect(a.data) })
  167. })({
  168. apply({ a, params }, ctx: PluginContext) {
  169. return Task.create('Custom Props', async taskCtx => {
  170. await attachProps(a.data, ctx, taskCtx, params.properties);
  171. return new SO.Molecule.Model(a.data, { label: 'Props', description: `${params.properties.length} Selected` });
  172. });
  173. }
  174. });
  175. async function attachProps(model: Model, ctx: PluginContext, taskCtx: RuntimeContext, names: string[]) {
  176. for (const name of names) {
  177. const p = ctx.customModelProperties.get(name);
  178. await p.attach(model).runInContext(taskCtx);
  179. }
  180. }
  181. export { ShapeFromPly }
  182. type ShapeFromPly = typeof ShapeFromPly
  183. const ShapeFromPly = PluginStateTransform.BuiltIn({
  184. name: 'shape-from-ply',
  185. display: { name: 'Shape from PLY', description: 'Create Shape from PLY data' },
  186. from: SO.Format.Ply,
  187. to: SO.Shape.Provider,
  188. params(a) {
  189. return { };
  190. }
  191. })({
  192. apply({ a, params }) {
  193. return Task.create('Create shape from PLY', async ctx => {
  194. const shape = await shapeFromPly(a.data, params).runInContext(ctx)
  195. const props = { label: 'Shape' };
  196. return new SO.Shape.Provider(shape, props);
  197. });
  198. }
  199. });