structure.ts 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  1. /**
  2. * Copyright (c) 2019-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
  3. *
  4. * @author Alexander Rose <alexander.rose@weirdbyte.de>
  5. */
  6. import { StateElements, AssemblyNames, StructureViewerState, ModelNames } from '../types';
  7. import { PluginCommands } from 'molstar/lib/mol-plugin/commands';
  8. import { StateBuilder, State, StateSelection } from 'molstar/lib/mol-state';
  9. import { StateTransforms } from 'molstar/lib/mol-plugin-state/transforms';
  10. import { Vec3 } from 'molstar/lib/mol-math/linear-algebra';
  11. import { PluginContext } from 'molstar/lib/mol-plugin/context';
  12. import { PluginStateObject as PSO } from 'molstar/lib/mol-plugin-state/objects';
  13. import { AssemblySymmetryProvider } from 'molstar/lib/mol-model-props/rcsb/assembly-symmetry';
  14. import { Task } from 'molstar/lib/mol-task';
  15. import { AssemblySymmetry3D } from 'molstar/lib/mol-plugin/behavior/dynamic/custom-props/rcsb/assembly-symmetry';
  16. import { ValidationReportProvider } from 'molstar/lib/mol-model-props/rcsb/validation-report';
  17. import { getStructureSize } from './util';
  18. export class StructureView {
  19. get customState() {
  20. return this.plugin.customState as StructureViewerState
  21. }
  22. async applyState(tree: StateBuilder) {
  23. await PluginCommands.State.Update(this.plugin, { state: this.plugin.state.dataState, tree });
  24. }
  25. get experimentalData () {
  26. return this.customState.volumeData
  27. }
  28. findTrajectoryRef() {
  29. const trajectories = this.plugin.state.dataState.select(StateSelection.Generators.rootsOfType(PSO.Molecule.Trajectory))
  30. return trajectories.length > 0 ? trajectories[0].transform.ref : ''
  31. }
  32. getAssembly() {
  33. const trajectoryRef = this.findTrajectoryRef()
  34. if (!trajectoryRef || !this.plugin.state.dataState.transforms.has(trajectoryRef)) return
  35. const assemblies = this.plugin.state.dataState.select(StateSelection.Generators.rootsOfType(PSO.Molecule.Structure, trajectoryRef))
  36. return assemblies.length > 0 ? assemblies[0] : undefined
  37. }
  38. getModel() {
  39. const trajectoryRef = this.findTrajectoryRef()
  40. const models = this.plugin.state.dataState.select(StateSelection.Generators.rootsOfType(PSO.Molecule.Model, trajectoryRef))
  41. return models.length > 0 ? models[0].obj : undefined
  42. }
  43. getSize() {
  44. const assembly = this.getAssembly()
  45. return assembly?.obj && getStructureSize(assembly.obj.data)
  46. }
  47. private ensureModelUnitcell(tree: StateBuilder.Root, state: State) {
  48. if (!state.tree.transforms.has(StateElements.ModelUnitcell)) {
  49. tree.to(StateElements.Model).apply(
  50. StateTransforms.Representation.ModelUnitcell3D,
  51. undefined, { ref: StateElements.ModelUnitcell }
  52. )
  53. }
  54. }
  55. async attachAssemblySymmetry() {
  56. const assembly = this.getAssembly()?.obj
  57. if (!assembly || assembly.data.isEmpty) return
  58. await this.plugin.runTask(Task.create('Assembly symmetry', async runtime => {
  59. await AssemblySymmetryProvider.attach({ fetch: this.plugin.fetch, runtime }, assembly.data)
  60. }))
  61. }
  62. async attachValidationReport() {
  63. const model = this.getModel()
  64. if (!model) return
  65. await this.plugin.runTask(Task.create('Validation Report', async runtime => {
  66. await ValidationReportProvider.attach({ fetch: this.plugin.fetch, runtime }, model.data)
  67. }))
  68. }
  69. async setAssembly(id: string) {
  70. const state = this.plugin.state.dataState;
  71. const tree = state.build();
  72. if (state.tree.transforms.has(StateElements.Assembly)) {
  73. tree.to(StateElements.Assembly).update(
  74. StateTransforms.Model.StructureFromModel,
  75. props => ({ ...props, ...getAssemblyProps(id) })
  76. )
  77. } else {
  78. tree.to(StateElements.Model).apply(
  79. StateTransforms.Model.StructureFromModel,
  80. getAssemblyProps(id), { ref: StateElements.Assembly, tags: getAssemblyTag(id) }
  81. )
  82. }
  83. if (id === AssemblyNames.Unitcell || id === AssemblyNames.Supercell) {
  84. this.ensureModelUnitcell(tree, state)
  85. }
  86. await this.applyState(tree)
  87. await this.attachAssemblySymmetry()
  88. await this.experimentalData.init()
  89. }
  90. async setModel(modelIndex: number) {
  91. const state = this.plugin.state.dataState;
  92. const tree = state.build();
  93. if (modelIndex === ModelNames.All) {
  94. tree.delete(StateElements.Model)
  95. .to(StateElements.Trajectory).apply(
  96. StateTransforms.Model.StructureFromTrajectory,
  97. {}, { ref: StateElements.Assembly }
  98. )
  99. } else {
  100. if (state.tree.transforms.has(StateElements.Model)) {
  101. tree.to(StateElements.Model).update(
  102. StateTransforms.Model.ModelFromTrajectory,
  103. props => ({ ...props, modelIndex })
  104. )
  105. } else {
  106. tree.delete(StateElements.Assembly)
  107. .to(StateElements.Trajectory).apply(
  108. StateTransforms.Model.ModelFromTrajectory,
  109. { modelIndex }, { ref: StateElements.Model }
  110. )
  111. }
  112. }
  113. await this.applyState(tree)
  114. }
  115. async setSymmetry(symmetryIndex: number) {
  116. const state = this.plugin.state.dataState;
  117. const tree = state.build();
  118. if (symmetryIndex === -1) {
  119. tree.delete(StateElements.AssemblySymmetry)
  120. } else {
  121. if (state.tree.transforms.has(StateElements.AssemblySymmetry)) {
  122. tree.to(StateElements.AssemblySymmetry).update(
  123. AssemblySymmetry3D,
  124. props => ({ ...props, symmetryIndex })
  125. )
  126. } else {
  127. const assembly = this.getAssembly()?.obj
  128. if (!assembly || assembly.data.isEmpty) return
  129. const props = AssemblySymmetry3D.createDefaultParams(assembly, this.plugin)
  130. tree.to(StateElements.Assembly).apply(
  131. AssemblySymmetry3D,
  132. { ...props, symmetryIndex }, { ref: StateElements.AssemblySymmetry }
  133. )
  134. }
  135. }
  136. await this.applyState(tree)
  137. }
  138. constructor(private plugin: PluginContext) {
  139. }
  140. }
  141. function getAssemblyProps(id: string) {
  142. if (id === AssemblyNames.Unitcell) {
  143. return {
  144. type: {
  145. name: 'symmetry' as const,
  146. params: { ijkMin: Vec3.create(0, 0, 0), ijkMax: Vec3.create(0, 0, 0) }
  147. }
  148. }
  149. } else if (id === AssemblyNames.Supercell) {
  150. return {
  151. type: {
  152. name: 'symmetry' as const,
  153. params: { ijkMin: Vec3.create(-1, -1, -1), ijkMax: Vec3.create(1, 1, 1) }
  154. }
  155. }
  156. } else if (id === AssemblyNames.CrystalContacts) {
  157. return {
  158. type: {
  159. name: 'symmetry-mates' as const,
  160. params: { radius: 5 }
  161. }
  162. }
  163. } else {
  164. return {
  165. type: {
  166. name: 'assembly' as const,
  167. params: { id }
  168. }
  169. }
  170. }
  171. }
  172. function getAssemblyTag(id: string) {
  173. switch (id) {
  174. case AssemblyNames.Unitcell:
  175. case AssemblyNames.Supercell:
  176. case AssemblyNames.CrystalContacts:
  177. return id
  178. default:
  179. return undefined
  180. }
  181. }