jolecule.ts 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  1. /**
  2. * Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
  3. *
  4. * @author David Sehnal <david.sehnal@gmail.com>
  5. */
  6. import { StateTree, StateBuilder, StateAction, State } from '../../../mol-state';
  7. import { StateTransforms } from '../../../mol-plugin/state/transforms';
  8. import { createModelTree, complexRepresentation } from '../../../mol-plugin/state/actions/structure';
  9. import { PluginContext } from '../../../mol-plugin/context';
  10. import { PluginStateObject } from '../../../mol-plugin/state/objects';
  11. import { ParamDefinition } from '../../../mol-util/param-definition';
  12. import { PluginCommands } from '../../../mol-plugin/command';
  13. import { Vec3 } from '../../../mol-math/linear-algebra';
  14. import { PluginStateSnapshotManager } from '../../../mol-plugin/state/snapshots';
  15. import { MolScriptBuilder as MS } from '../../../mol-script/language/builder';
  16. import { Text } from '../../../mol-geo/geometry/text/text';
  17. import { UUID } from '../../../mol-util';
  18. import { ColorNames } from '../../../mol-util/color/tables';
  19. import { Camera } from '../../../mol-canvas3d/camera';
  20. import { StructureRepresentation3DHelpers } from '../../../mol-plugin/state/transforms/representation';
  21. export const CreateJoleculeState = StateAction.build({
  22. display: { name: 'Jolecule State Import' },
  23. params: { id: ParamDefinition.Text('1mbo') },
  24. from: PluginStateObject.Root
  25. })(async ({ ref, state, params }, plugin: PluginContext) => {
  26. try {
  27. const id = params.id.trim().toLowerCase();
  28. const data = await plugin.runTask(plugin.fetch({ url: `https://jolecule.appspot.com/pdb/${id}.views.json`, type: 'json' })) as JoleculeSnapshot[];
  29. data.sort((a, b) => a.order - b.order);
  30. await PluginCommands.State.RemoveObject.dispatch(plugin, { state, ref });
  31. plugin.state.snapshots.clear();
  32. const template = createTemplate(plugin, state, id);
  33. const snapshots = data.map((e, idx) => buildSnapshot(plugin, template, { e, idx, len: data.length }));
  34. for (const s of snapshots) {
  35. plugin.state.snapshots.add(s);
  36. }
  37. PluginCommands.State.Snapshots.Apply.dispatch(plugin, { id: snapshots[0].snapshot.id });
  38. } catch (e) {
  39. plugin.log.error(`Jolecule Failed: ${e}`);
  40. }
  41. });
  42. interface JoleculeSnapshot {
  43. order: number,
  44. distances: { i_atom1: number, i_atom2: number }[],
  45. labels: { i_atom: number, text: string }[],
  46. camera: { up: Vec3, pos: Vec3, in: Vec3, slab: { z_front: number, z_back: number, zoom: number } },
  47. selected: number[],
  48. text: string
  49. }
  50. function createTemplate(plugin: PluginContext, state: State, id: string) {
  51. const b = new StateBuilder.Root(state.tree);
  52. const data = b.toRoot().apply(StateTransforms.Data.Download, { url: `https://www.ebi.ac.uk/pdbe/static/entry/${id}_updated.cif` }, { state: { isGhost: true }});
  53. const model = createModelTree(data, 'cif');
  54. const structure = model.apply(StateTransforms.Model.StructureFromModel, {});
  55. complexRepresentation(plugin, structure, { hideWater: true });
  56. return { tree: b.getTree(), structure: structure.ref };
  57. }
  58. const labelOptions: ParamDefinition.Values<Text.Params> = {
  59. ...ParamDefinition.getDefaultValues(Text.Params),
  60. tether: true,
  61. sizeFactor: 1.3,
  62. attachment: 'bottom-right',
  63. offsetZ: 10,
  64. background: true,
  65. backgroundMargin: 0.2,
  66. backgroundColor: ColorNames.skyblue,
  67. backgroundOpacity: 0.9
  68. }
  69. // const distanceLabelOptions = {
  70. // ...ParamDefinition.getDefaultValues(Text.Params),
  71. // sizeFactor: 1,
  72. // offsetX: 0,
  73. // offsetY: 0,
  74. // offsetZ: 10,
  75. // background: true,
  76. // backgroundMargin: 0.2,
  77. // backgroundColor: ColorNames.snow,
  78. // backgroundOpacity: 0.9
  79. // }
  80. function buildSnapshot(plugin: PluginContext, template: { tree: StateTree, structure: string }, params: { e: JoleculeSnapshot, idx: number, len: number }): PluginStateSnapshotManager.Entry {
  81. const b = new StateBuilder.Root(template.tree);
  82. let i = 0;
  83. for (const l of params.e.labels) {
  84. const query = createQuery([l.i_atom]);
  85. const group = b.to(template.structure)
  86. .group(StateTransforms.Misc.CreateGroup, { label: `Label ${++i}` });
  87. group
  88. .apply(StateTransforms.Model.StructureSelection, { query, label: 'Atom' })
  89. .apply(StateTransforms.Representation.StructureLabels3D, {
  90. target: { name: 'static-text', params: { value: l.text || '' } },
  91. options: labelOptions
  92. });
  93. group
  94. .apply(StateTransforms.Model.StructureSelection, { query: MS.struct.modifier.wholeResidues([query]), label: 'Residue' })
  95. .apply(StateTransforms.Representation.StructureRepresentation3D,
  96. StructureRepresentation3DHelpers.getDefaultParamsStatic(plugin, 'ball-and-stick', { }));
  97. }
  98. if (params.e.selected && params.e.selected.length > 0) {
  99. b.to(template.structure)
  100. .apply(StateTransforms.Model.StructureSelection, { query: createQuery(params.e.selected), label: `Selected` })
  101. .apply(StateTransforms.Representation.StructureRepresentation3D,
  102. StructureRepresentation3DHelpers.getDefaultParamsStatic(plugin, 'ball-and-stick'));
  103. }
  104. // TODO
  105. // for (const l of params.e.distances) {
  106. // b.to('structure')
  107. // .apply(StateTransforms.Model.StructureSelection, { query: createQuery([l.i_atom1, l.i_atom2]), label: `Distance ${++i}` })
  108. // .apply(StateTransforms.Representation.StructureLabels3D, {
  109. // target: { name: 'static-text', params: { value: l. || '' } },
  110. // options: labelOptions
  111. // });
  112. // }
  113. return PluginStateSnapshotManager.Entry({
  114. id: UUID.create22(),
  115. data: { tree: StateTree.toJSON(b.getTree()) },
  116. camera: {
  117. current: getCameraSnapshot(params.e.camera),
  118. transitionStyle: 'animate',
  119. transitionDurationInMs: 350
  120. }
  121. }, {
  122. name: params.e.text
  123. });
  124. }
  125. function getCameraSnapshot(e: JoleculeSnapshot['camera']): Camera.Snapshot {
  126. const direction = Vec3.sub(Vec3.zero(), e.pos, e.in);
  127. Vec3.normalize(direction, direction);
  128. const up = Vec3.sub(Vec3.zero(), e.pos, e.up);
  129. Vec3.normalize(up, up);
  130. const s: Camera.Snapshot = {
  131. mode: 'perspective',
  132. position: Vec3.scaleAndAdd(Vec3.zero(), e.pos, direction, e.slab.zoom),
  133. target: e.pos,
  134. direction,
  135. up,
  136. near: e.slab.zoom + e.slab.z_front,
  137. far: e.slab.zoom + e.slab.z_back,
  138. fogNear: e.slab.zoom + e.slab.z_front,
  139. fogFar: e.slab.zoom + e.slab.z_back,
  140. fov: Math.PI / 4,
  141. zoom: 1
  142. };
  143. return s;
  144. }
  145. function createQuery(atomIndices: number[]) {
  146. if (atomIndices.length === 0) return MS.struct.generator.empty();
  147. return MS.struct.generator.atomGroups({
  148. 'atom-test': atomIndices.length === 1
  149. ? MS.core.rel.eq([MS.struct.atomProperty.core.sourceIndex(), atomIndices[0]])
  150. : MS.core.set.has([MS.set.apply(null, atomIndices), MS.struct.atomProperty.core.sourceIndex()]),
  151. 'group-by': 0
  152. });
  153. }