structure-representation-helper.ts 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241
  1. /**
  2. * Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
  3. *
  4. * @author Alexander Rose <alexander.rose@weirdbyte.de>
  5. */
  6. import { PluginStateObject as PSO } from '../../mol-plugin/state/objects';
  7. import { StateTransforms } from '../../mol-plugin/state/transforms';
  8. import { StateTransformer, StateSelection, StateObjectCell, StateTransform, StateBuilder } from '../../mol-state';
  9. import { StructureElement, Structure, StructureSelection, QueryContext } from '../../mol-model/structure';
  10. import { PluginContext } from '../context';
  11. import { StructureRepresentation3DHelpers } from '../state/transforms/representation';
  12. import Expression from '../../mol-script/language/expression';
  13. import { compile } from '../../mol-script/runtime/query/compiler';
  14. import { StructureSelectionQueries as Q } from '../util/structure-selection-helper';
  15. import { MolScriptBuilder as MS } from '../../mol-script/language/builder';
  16. import { VisualQuality } from '../../mol-geo/geometry/base';
  17. type StructureTransform = StateObjectCell<PSO.Molecule.Structure, StateTransform<StateTransformer<any, PSO.Molecule.Structure, any>>>
  18. type RepresentationTransform = StateObjectCell<PSO.Molecule.Structure.Representation3D, StateTransform<StateTransformer<any, PSO.Molecule.Structure.Representation3D, any>>>
  19. const RepresentationManagerTag = 'representation-controls'
  20. export function getRepresentationManagerTag(type: string) {
  21. return `${RepresentationManagerTag}-${type}`
  22. }
  23. function getCombinedLoci(mode: SelectionModifier, loci: StructureElement.Loci, currentLoci: StructureElement.Loci): StructureElement.Loci {
  24. switch (mode) {
  25. case 'add': return StructureElement.Loci.union(loci, currentLoci)
  26. case 'remove': return StructureElement.Loci.subtract(currentLoci, loci)
  27. case 'only': return loci
  28. }
  29. }
  30. type SelectionModifier = 'add' | 'remove' | 'only'
  31. export class StructureRepresentationHelper {
  32. getRepresentationStructure(rootRef: string, type: string) {
  33. const state = this.plugin.state.dataState
  34. const selections = state.select(StateSelection.Generators.ofType(PSO.Molecule.Structure, rootRef).withTag(getRepresentationManagerTag(type)));
  35. return selections.length > 0 ? selections[0] : undefined
  36. }
  37. getRepresentation(rootRef: string, type: string) {
  38. const reprStructure = this.getRepresentationStructure(rootRef, type)
  39. if (!reprStructure) return
  40. const state = this.plugin.state.dataState
  41. const selections = state.select(StateSelection.Generators.ofType(PSO.Molecule.Structure.Representation3D, reprStructure.transform.ref))
  42. return selections.length > 0 ? selections[0] : undefined
  43. }
  44. private async _set(modifier: SelectionModifier, type: string, loci: StructureElement.Loci, structure: StructureTransform, props = {}) {
  45. const state = this.plugin.state.dataState
  46. const update = state.build()
  47. const s = structure.obj!.data
  48. const reprStructure = this.getRepresentationStructure(structure.transform.ref, type)
  49. if (reprStructure) {
  50. const currentLoci = StructureElement.Bundle.toLoci(reprStructure.params!.values.bundle, s)
  51. const combinedLoci = getCombinedLoci(modifier, loci, currentLoci)
  52. update.to(reprStructure).update({
  53. ...reprStructure.params!.values,
  54. bundle: StructureElement.Bundle.fromLoci(combinedLoci)
  55. })
  56. } else {
  57. const combinedLoci = getCombinedLoci(modifier, loci, StructureElement.Loci.none(s))
  58. const params = StructureRepresentation3DHelpers.getDefaultParams(this.plugin, type as any, s)
  59. const p = params.type.params
  60. Object.assign(p, props)
  61. if (p.ignoreHydrogens !== undefined) p.ignoreHydrogens = this._ignoreHydrogens
  62. if (p.quality !== undefined) p.quality = this._quality
  63. update.to(structure.transform.ref)
  64. .apply(
  65. StateTransforms.Model.StructureSelectionFromBundle,
  66. { bundle: StructureElement.Bundle.fromLoci(combinedLoci), label: type },
  67. { tags: [ RepresentationManagerTag, getRepresentationManagerTag(type) ] }
  68. )
  69. .apply( StateTransforms.Representation.StructureRepresentation3D, params)
  70. }
  71. await this.plugin.runTask(state.updateTree(update, { doNotUpdateCurrent: true }))
  72. }
  73. async set(modifier: SelectionModifier, type: string, lociGetter: (structure: Structure) => StructureElement.Loci, props = {}) {
  74. const state = this.plugin.state.dataState;
  75. const structures = state.select(StateSelection.Generators.rootsOfType(PSO.Molecule.Structure))
  76. for (const structure of structures) {
  77. const s = structure.obj!.data
  78. const loci = lociGetter(s)
  79. await this._set(modifier, type, loci, structure, props)
  80. }
  81. }
  82. async setFromExpression(modifier: SelectionModifier, type: string, expression: Expression, props = {}) {
  83. return this.set(modifier, type, (structure) => {
  84. const compiled = compile<StructureSelection>(expression)
  85. const result = compiled(new QueryContext(structure))
  86. return StructureSelection.toLociWithSourceUnits(result)
  87. }, props)
  88. }
  89. async clear() {
  90. const { registry } = this.plugin.structureRepresentation
  91. const state = this.plugin.state.dataState;
  92. const update = state.build()
  93. const structures = state.select(StateSelection.Generators.rootsOfType(PSO.Molecule.Structure))
  94. const bundle = StructureElement.Bundle.Empty
  95. for (const structure of structures) {
  96. for (let i = 0, il = registry.types.length; i < il; ++i) {
  97. const type = registry.types[i][0]
  98. const reprStructure = this.getRepresentationStructure(structure.transform.ref, type)
  99. if (reprStructure) {
  100. update.to(reprStructure).update({ ...reprStructure.params!.values, bundle })
  101. }
  102. }
  103. }
  104. await this.plugin.runTask(state.updateTree(update, { doNotUpdateCurrent: true }))
  105. }
  106. async eachRepresentation(callback: (repr: RepresentationTransform, update: StateBuilder.Root) => void) {
  107. const { registry } = this.plugin.structureRepresentation
  108. const state = this.plugin.state.dataState;
  109. const update = state.build()
  110. const structures = state.select(StateSelection.Generators.rootsOfType(PSO.Molecule.Structure))
  111. for (const structure of structures) {
  112. for (let i = 0, il = registry.types.length; i < il; ++i) {
  113. const repr = this.getRepresentation(structure.transform.ref, registry.types[i][0])
  114. if (repr) callback(repr, update)
  115. }
  116. }
  117. await this.plugin.runTask(state.updateTree(update, { doNotUpdateCurrent: true }))
  118. }
  119. private _ignoreHydrogens = false
  120. get ignoreHydrogens () { return this._ignoreHydrogens }
  121. async setIgnoreHydrogens(ignoreHydrogens: boolean) {
  122. if (ignoreHydrogens === this._ignoreHydrogens) return
  123. await this.eachRepresentation((repr, update) => {
  124. if (repr.params && repr.params.values.type.params.ignoreHydrogens !== undefined) {
  125. const { name, params } = repr.params.values.type
  126. update.to(repr.transform.ref).update(
  127. StateTransforms.Representation.StructureRepresentation3D,
  128. props => ({ ...props, type: { name, params: { ...params, ignoreHydrogens }}})
  129. )
  130. }
  131. })
  132. this._ignoreHydrogens = ignoreHydrogens
  133. }
  134. private _quality = 'auto' as VisualQuality
  135. get quality () { return this._quality }
  136. async setQuality(quality: VisualQuality) {
  137. if (quality === this._quality) return
  138. await this.eachRepresentation((repr, update) => {
  139. if (repr.params && repr.params.values.type.params.quality !== undefined) {
  140. const { name, params } = repr.params.values.type
  141. update.to(repr.transform.ref).update(
  142. StateTransforms.Representation.StructureRepresentation3D,
  143. props => ({ ...props, type: { name, params: { ...params, quality }}})
  144. )
  145. }
  146. })
  147. this._quality = quality
  148. }
  149. async preset() {
  150. // TODO option to limit to specific structure
  151. const state = this.plugin.state.dataState;
  152. const structures = state.select(StateSelection.Generators.rootsOfType(PSO.Molecule.Structure))
  153. if (structures.length === 0) return
  154. const s = structures[0].obj!.data
  155. if (s.elementCount < 50000) {
  156. await polymerAndLigand(this)
  157. } else if (s.elementCount < 200000) {
  158. await proteinAndNucleic(this)
  159. } else {
  160. if (s.unitSymmetryGroups[0].units.length > 10) {
  161. await capsid(this)
  162. } else {
  163. await coarseCapsid(this)
  164. }
  165. }
  166. }
  167. constructor(private plugin: PluginContext) {
  168. }
  169. }
  170. //
  171. async function polymerAndLigand(r: StructureRepresentationHelper) {
  172. await r.clear()
  173. await r.setFromExpression('add', 'cartoon', Q.all)
  174. await r.setFromExpression('add', 'carbohydrate', Q.all)
  175. await r.setFromExpression('add', 'ball-and-stick', MS.struct.modifier.union([
  176. MS.struct.combinator.merge([ Q.ligandPlusConnected, Q.branchedConnectedOnly, Q.water ])
  177. ]))
  178. }
  179. async function proteinAndNucleic(r: StructureRepresentationHelper) {
  180. await r.clear()
  181. await r.setFromExpression('add', 'cartoon', Q.protein)
  182. await r.setFromExpression('add', 'gaussian-surface', Q.nucleic)
  183. await r.setFromExpression('add', 'carbohydrate', Q.all)
  184. await r.setFromExpression('add', 'ball-and-stick', MS.struct.modifier.union([
  185. MS.struct.combinator.merge([ Q.ligandPlusConnected, Q.branchedConnectedOnly, Q.water ])
  186. ]))
  187. }
  188. async function capsid(r: StructureRepresentationHelper) {
  189. await r.clear()
  190. await r.setFromExpression('add', 'gaussian-surface', Q.polymer, {
  191. smoothness: 0.5,
  192. })
  193. }
  194. async function coarseCapsid(r: StructureRepresentationHelper) {
  195. await r.clear()
  196. await r.setFromExpression('add', 'gaussian-surface', Q.trace, {
  197. radiusOffset: 1,
  198. smoothness: 0.5,
  199. visuals: ['structure-gaussian-surface-mesh']
  200. })
  201. }
  202. export const StructureRepresentationPresets = {
  203. polymerAndLigand,
  204. proteinAndNucleic,
  205. capsid,
  206. coarseCapsid
  207. }