structure.ts 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  1. /**
  2. * Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
  3. *
  4. * @author David Sehnal <david.sehnal@gmail.com>
  5. */
  6. import { PluginContext } from '../../mol-plugin/context';
  7. import { StateObjectRef, StateObjectSelector, StateTransformer, StateTransform, StateObjectCell } from '../../mol-state';
  8. import { PluginStateObject as SO } from '../objects';
  9. import { StateTransforms } from '../transforms';
  10. import { RootStructureDefinition } from '../helpers/root-structure';
  11. import { StructureComponentParams, StaticStructureComponentType } from '../helpers/structure-component';
  12. import { BuildInTrajectoryFormat, TrajectoryFormatProvider } from '../formats/trajectory';
  13. import { StructureRepresentationBuilder } from './structure/representation';
  14. import { StructureSelectionQuery } from '../helpers/structure-selection-query';
  15. import { Task } from '../../mol-task';
  16. import { StructureElement } from '../../mol-model/structure';
  17. export type TrajectoryFormat = 'pdb' | 'cif' | 'gro' | '3dg'
  18. export enum StructureBuilderTags {
  19. Trajectory = 'trajectory',
  20. Model = 'model',
  21. ModelProperties = 'model-properties',
  22. Structure = 'structure',
  23. StructureProperties = 'structure-properties',
  24. Component = 'structure-component'
  25. }
  26. export class StructureBuilder {
  27. private get dataState() {
  28. return this.plugin.state.dataState;
  29. }
  30. private async parseTrajectoryData(data: StateObjectRef<SO.Data.Binary | SO.Data.String>, format: BuildInTrajectoryFormat | TrajectoryFormatProvider) {
  31. const provider = typeof format === 'string' ? this.plugin.dataFormat.trajectory.get(format) : format;
  32. if (!provider) throw new Error(`'${format}' is not a supported data format.`);
  33. const { trajectory } = await provider.parse(this.plugin, data, { trajectoryTags: StructureBuilderTags.Trajectory });
  34. return trajectory;
  35. }
  36. private async parseTrajectoryBlob(data: StateObjectRef<SO.Data.Blob>, params: StateTransformer.Params<StateTransforms['Data']['ParseBlob']>) {
  37. const state = this.dataState;
  38. const trajectory = state.build().to(data)
  39. .apply(StateTransforms.Data.ParseBlob, params, { state: { isGhost: true } })
  40. .apply(StateTransforms.Model.TrajectoryFromBlob, void 0, { tags: StructureBuilderTags.Trajectory });
  41. await this.plugin.runTask(this.dataState.updateTree(trajectory, { revertOnError: true }));
  42. return trajectory.selector;
  43. }
  44. readonly representation = new StructureRepresentationBuilder(this.plugin);
  45. async parseStructure(params: {
  46. data?: StateObjectRef<SO.Data.Binary | SO.Data.String>,
  47. dataFormat?: BuildInTrajectoryFormat | TrajectoryFormatProvider,
  48. blob?: StateObjectRef<SO.Data.Blob>
  49. blobParams?: StateTransformer.Params<StateTransforms['Data']['ParseBlob']>,
  50. model?: StateTransformer.Params<StateTransforms['Model']['ModelFromTrajectory']>,
  51. modelProperties?: boolean | StateTransformer.Params<StateTransforms['Model']['CustomModelProperties']>,
  52. structure?: RootStructureDefinition.Params,
  53. structureProperties?: boolean | StateTransformer.Params<StateTransforms['Model']['CustomStructureProperties']>
  54. }) {
  55. const trajectory = params.data
  56. ? await this.parseTrajectory(params.data, params.dataFormat! || 'cif')
  57. : await this.parseTrajectoryBlob(params.blob!, params.blobParams!);
  58. const model = await this.createModel(trajectory, params.model);
  59. const modelProperties = !!params.modelProperties
  60. ? await this.insertModelProperties(model, typeof params?.modelProperties !== 'boolean' ? params?.modelProperties : void 0) : void 0;
  61. const structure = await this.createStructure(modelProperties || model, params.structure);
  62. const structureProperties = !!params.structureProperties
  63. ? await this.insertStructureProperties(structure, typeof params?.structureProperties !== 'boolean' ? params?.structureProperties : void 0) : void 0;
  64. return {
  65. trajectory,
  66. model: modelProperties || model,
  67. modelRoot: model,
  68. modelProperties,
  69. structure: structureProperties || structure,
  70. structureRoot: structure,
  71. structureProperties
  72. };
  73. }
  74. async parseTrajectory(data: StateObjectRef<SO.Data.Binary | SO.Data.String>, format: BuildInTrajectoryFormat | TrajectoryFormatProvider): Promise<StateObjectSelector<SO.Molecule.Trajectory>>
  75. async parseTrajectory(blob: StateObjectRef<SO.Data.Blob>, params: StateTransformer.Params<StateTransforms['Data']['ParseBlob']>): Promise<StateObjectSelector<SO.Molecule.Trajectory>>
  76. async parseTrajectory(data: StateObjectRef, params: any) {
  77. const cell = StateObjectRef.resolveAndCheck(this.dataState, data as StateObjectRef);
  78. if (!cell) throw new Error('Invalid data cell.');
  79. if (SO.Data.Blob.is(cell.obj)) {
  80. return this.parseTrajectoryBlob(data, params);
  81. } else {
  82. return this.parseTrajectoryData(data, params);
  83. }
  84. }
  85. async createModel(trajectory: StateObjectRef<SO.Molecule.Trajectory>, params?: StateTransformer.Params<StateTransforms['Model']['ModelFromTrajectory']>, initialState?: Partial<StateTransform.State>) {
  86. const state = this.dataState;
  87. const model = state.build().to(trajectory)
  88. .apply(StateTransforms.Model.ModelFromTrajectory, params || { modelIndex: 0 }, { tags: StructureBuilderTags.Model, state: initialState });
  89. await this.plugin.runTask(this.dataState.updateTree(model, { revertOnError: true }));
  90. return model.selector;
  91. }
  92. async insertModelProperties(model: StateObjectRef<SO.Molecule.Model>, params?: StateTransformer.Params<StateTransforms['Model']['CustomModelProperties']>) {
  93. const state = this.dataState;
  94. const props = state.build().to(model)
  95. .insert(StateTransforms.Model.CustomModelProperties, params, { tags: StructureBuilderTags.ModelProperties, isDecorator: true });
  96. await this.plugin.runTask(this.dataState.updateTree(props, { revertOnError: true }));
  97. return props.selector;
  98. }
  99. async createStructure(model: StateObjectRef<SO.Molecule.Model>, params?: RootStructureDefinition.Params, initialState?: Partial<StateTransform.State>) {
  100. const state = this.dataState;
  101. const structure = state.build().to(model)
  102. .apply(StateTransforms.Model.StructureFromModel, { type: params || { name: 'assembly', params: { } } }, { tags: StructureBuilderTags.Structure, state: initialState });
  103. await this.plugin.runTask(this.dataState.updateTree(structure, { revertOnError: true }));
  104. return structure.selector;
  105. }
  106. async insertStructureProperties(structure: StateObjectRef<SO.Molecule.Structure>, params?: StateTransformer.Params<StateTransforms['Model']['CustomStructureProperties']>) {
  107. const state = this.dataState;
  108. const props = state.build().to(structure)
  109. .insert(StateTransforms.Model.CustomStructureProperties, params, { tags: StructureBuilderTags.StructureProperties, isDecorator: true });
  110. await this.plugin.runTask(this.dataState.updateTree(props, { revertOnError: true }));
  111. return props.selector;
  112. }
  113. isComponent(cell: StateObjectCell) {
  114. return cell.transform.transformer === StateTransforms.Model.StructureComponent;
  115. }
  116. /** returns undefined if the component is empty/null */
  117. async tryCreateComponent(structure: StateObjectRef<SO.Molecule.Structure>, params: StructureComponentParams, key: string, tags?: string[]): Promise<StateObjectRef<SO.Molecule.Structure> | undefined> {
  118. const state = this.dataState;
  119. const root = state.build().to(structure);
  120. const keyTag = `structure-component-${key}`;
  121. const component = root.applyOrUpdateTagged(keyTag, StateTransforms.Model.StructureComponent, params, {
  122. tags: tags ? [...tags, StructureBuilderTags.Component, keyTag] : [StructureBuilderTags.Component, keyTag]
  123. });
  124. await this.plugin.runTask(this.dataState.updateTree(component));
  125. const selector = component.selector;
  126. if (!selector.isOk || selector.cell?.obj?.data.elementCount === 0) {
  127. const del = state.build().delete(selector.ref);
  128. await this.plugin.runTask(this.dataState.updateTree(del));
  129. return;
  130. }
  131. return selector;
  132. }
  133. tryCreateStaticComponent(params: { structure: StateObjectRef<SO.Molecule.Structure>, type: StaticStructureComponentType, key: string, label?: string, tags?: string[] }) {
  134. return this.tryCreateComponent(params.structure, {
  135. type: { name: 'static', params: params.type },
  136. nullIfEmpty: true,
  137. label: ''
  138. }, params.key, params.tags);
  139. }
  140. tryCreateQueryComponent(params: { structure: StateObjectRef<SO.Molecule.Structure>, query: StructureSelectionQuery, key: string, label?: string, tags?: string[] }): Promise<StateObjectRef<SO.Molecule.Structure> | undefined> {
  141. return this.plugin.runTask(Task.create('Query Component', async taskCtx => {
  142. let { structure, query, key, label, tags } = params;
  143. label = (label || '').trim();
  144. const structureData = StateObjectRef.resolveAndCheck(this.dataState, structure)?.obj?.data;
  145. if (!structureData) return;
  146. const transformParams: StructureComponentParams = query.referencesCurrent
  147. ? {
  148. type: {
  149. name: 'bundle',
  150. params: StructureElement.Bundle.fromSelection(await query.getSelection(this.plugin, taskCtx, structureData)) },
  151. nullIfEmpty: true,
  152. label: label || query.label
  153. } : {
  154. type: { name: 'expression', params: query.expression },
  155. nullIfEmpty: true,
  156. label: label || query.label
  157. };
  158. if (query.ensureCustomProperties) {
  159. await query.ensureCustomProperties({ fetch: this.plugin.fetch, runtime: taskCtx }, structureData);
  160. }
  161. const state = this.dataState;
  162. const root = state.build().to(structure);
  163. const keyTag = `structure-component-${key}`;
  164. const component = root.applyOrUpdateTagged(keyTag, StateTransforms.Model.StructureComponent, transformParams, {
  165. tags: tags ? [...tags, StructureBuilderTags.Component, keyTag] : [StructureBuilderTags.Component, keyTag]
  166. });
  167. await this.dataState.updateTree(component).runInContext(taskCtx);
  168. const selector = component.selector;
  169. if (!selector.isOk || selector.cell?.obj?.data.elementCount === 0) {
  170. const del = state.build().delete(selector.ref);
  171. await this.plugin.runTask(this.dataState.updateTree(del));
  172. return;
  173. }
  174. return selector;
  175. }))
  176. }
  177. constructor(public plugin: PluginContext) {
  178. }
  179. }