structure-representation-helper.ts 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277
  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 { 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 { ParamDefinition as PD } from '../../mol-util/param-definition';
  12. import { StructureRepresentation3DHelpers } from '../../mol-plugin-state/transforms/representation';
  13. import Expression from '../../mol-script/language/expression';
  14. import { compile } from '../../mol-script/runtime/query/compiler';
  15. import { VisualQuality } from '../../mol-geo/geometry/base';
  16. import { InteractionsProps } from '../../mol-model-props/computed/interactions/interactions';
  17. import { InteractionsProvider } from '../../mol-model-props/computed/interactions';
  18. type StructureTransform = StateObjectCell<PSO.Molecule.Structure, StateTransform<StateTransformer<any, PSO.Molecule.Structure, any>>>
  19. type RepresentationTransform = StateObjectCell<PSO.Molecule.Structure.Representation3D, StateTransform<StateTransformer<any, PSO.Molecule.Structure.Representation3D, any>>>
  20. const RepresentationManagerTag = 'representation-controls'
  21. export function getRepresentationManagerTag(type: string) {
  22. return `${RepresentationManagerTag}-${type}`
  23. }
  24. function getCombinedLoci(mode: SelectionModifier, loci: StructureElement.Loci, currentLoci: StructureElement.Loci): StructureElement.Loci {
  25. switch (mode) {
  26. case 'add': return StructureElement.Loci.union(loci, currentLoci)
  27. case 'remove': return StructureElement.Loci.subtract(currentLoci, loci)
  28. case 'only': return loci
  29. }
  30. }
  31. type SelectionModifier = 'add' | 'remove' | 'only'
  32. type ReprProps = {
  33. repr?: {},
  34. color?: string | [string, {}],
  35. size?: string | [string, {}],
  36. }
  37. export class StructureRepresentationHelper {
  38. getRepresentationStructure(rootRef: string, type: string) {
  39. const state = this.plugin.state.dataState
  40. const selections = state.select(StateSelection.Generators.ofType(PSO.Molecule.Structure, rootRef).withTag(getRepresentationManagerTag(type)));
  41. return selections.length > 0 ? selections[0] : undefined
  42. }
  43. getRepresentation(rootRef: string, type: string) {
  44. const reprStructure = this.getRepresentationStructure(rootRef, type)
  45. if (!reprStructure) return
  46. const state = this.plugin.state.dataState
  47. const selections = state.select(StateSelection.Generators.ofType(PSO.Molecule.Structure.Representation3D, reprStructure.transform.ref))
  48. return selections.length > 0 ? selections[0] : undefined
  49. }
  50. private getRepresentationParams(structure: Structure, type: string, repr: RepresentationTransform | undefined, props: ReprProps = {}) {
  51. const reprProps = {
  52. ...(repr?.params && repr.params.values.type.params),
  53. ignoreHydrogens: this._ignoreHydrogens,
  54. quality: this._quality,
  55. ...props.repr
  56. }
  57. const { themeCtx } = this.plugin.structureRepresentation
  58. const p: StructureRepresentation3DHelpers.Props = {
  59. repr: [
  60. this.plugin.structureRepresentation.registry.get(type),
  61. () => reprProps
  62. ]
  63. }
  64. if (props.color) {
  65. const colorType = props.color instanceof Array ? props.color[0] : props.color
  66. const colorTheme = themeCtx.colorThemeRegistry.get(colorType)
  67. const colorProps = {
  68. ...(repr?.params && repr.params.values.colorTheme.name === colorType && repr.params.values.colorTheme.params),
  69. ...(props.color instanceof Array ? props.color[1] : {})
  70. }
  71. p.color = [colorTheme, () => colorProps]
  72. }
  73. if (props.size) {
  74. const sizeType = props.size instanceof Array ? props.size[0] : props.size
  75. const sizeTheme = themeCtx.sizeThemeRegistry.get(sizeType)
  76. const sizeProps = {
  77. ...(repr?.params && repr.params.values.sizeTheme.name === sizeType && repr.params.values.sizeTheme.params),
  78. ...(props.size instanceof Array ? props.size[1] : {})
  79. }
  80. p.size = [sizeTheme, () => sizeProps]
  81. }
  82. if (props.size) p.size = props.size
  83. return StructureRepresentation3DHelpers.createParams(this.plugin, structure, p)
  84. }
  85. private async _set(modifier: SelectionModifier, type: string, loci: StructureElement.Loci, structure: StructureTransform, props: ReprProps = {}) {
  86. const state = this.plugin.state.dataState
  87. const update = state.build()
  88. const s = structure.obj!.data
  89. const repr = this.getRepresentation(structure.transform.ref, type)
  90. const reprStructure = this.getRepresentationStructure(structure.transform.ref, type)
  91. const reprParams = this.getRepresentationParams(s.root, type, repr, props)
  92. if (reprStructure) {
  93. const currentLoci = StructureElement.Bundle.toLoci(reprStructure.params!.values.bundle, s)
  94. const combinedLoci = getCombinedLoci(modifier, loci, currentLoci)
  95. update.to(reprStructure).update({
  96. ...reprStructure.params!.values,
  97. bundle: StructureElement.Bundle.fromLoci(combinedLoci)
  98. })
  99. if (repr) update.to(repr).update(reprParams)
  100. } else {
  101. const combinedLoci = getCombinedLoci(modifier, loci, StructureElement.Loci.none(s))
  102. update.to(structure.transform.ref)
  103. .apply(
  104. StateTransforms.Model.StructureSelectionFromBundle,
  105. { bundle: StructureElement.Bundle.fromLoci(combinedLoci), label: type },
  106. { tags: [ RepresentationManagerTag, getRepresentationManagerTag(type) ] }
  107. )
  108. .apply(StateTransforms.Representation.StructureRepresentation3D, reprParams)
  109. }
  110. await this.plugin.runTask(state.updateTree(update, { doNotUpdateCurrent: true }))
  111. }
  112. async set(modifier: SelectionModifier, type: string, lociGetter: (structure: Structure) => StructureElement.Loci, props: ReprProps = {}) {
  113. const state = this.plugin.state.dataState;
  114. const structures = state.select(StateSelection.Generators.rootsOfType(PSO.Molecule.Structure))
  115. for (const structure of structures) {
  116. const s = structure.obj!.data
  117. const loci = lociGetter(s)
  118. await this._set(modifier, type, loci, structure, props)
  119. }
  120. }
  121. async setFromExpression(modifier: SelectionModifier, type: string, expression: Expression, props: ReprProps = {}) {
  122. return this.set(modifier, type, (structure) => {
  123. const compiled = compile<StructureSelection>(expression)
  124. const result = compiled(new QueryContext(structure))
  125. return StructureSelection.toLociWithSourceUnits(result)
  126. }, props)
  127. }
  128. async eachStructure(callback: (structure: StructureTransform, type: string, update: StateBuilder.Root) => void) {
  129. const { registry } = this.plugin.structureRepresentation
  130. const state = this.plugin.state.dataState;
  131. const update = state.build()
  132. const structures = state.select(StateSelection.Generators.rootsOfType(PSO.Molecule.Structure))
  133. for (const structure of structures) {
  134. for (let i = 0, il = registry.types.length; i < il; ++i) {
  135. const type = registry.types[i][0]
  136. const reprStructure = this.getRepresentationStructure(structure.transform.ref, type)
  137. if (reprStructure) callback(reprStructure, type, update)
  138. }
  139. }
  140. await this.plugin.runTask(state.updateTree(update, { doNotUpdateCurrent: true }))
  141. }
  142. async clear() {
  143. const bundle = StructureElement.Bundle.Empty
  144. await this.eachStructure((structure, type, update) => {
  145. update.to(structure).update({ ...structure.params!.values, bundle })
  146. })
  147. }
  148. async clearExcept(exceptTypes: string[]) {
  149. const bundle = StructureElement.Bundle.Empty
  150. await this.eachStructure((structure, type, update) => {
  151. if (!exceptTypes.includes(type)) {
  152. update.to(structure).update({ ...structure.params!.values, bundle })
  153. }
  154. })
  155. }
  156. async eachRepresentation(callback: (repr: RepresentationTransform, type: string, update: StateBuilder.Root) => void) {
  157. const { registry } = this.plugin.structureRepresentation
  158. const state = this.plugin.state.dataState;
  159. const update = state.build()
  160. const structures = state.select(StateSelection.Generators.rootsOfType(PSO.Molecule.Structure))
  161. for (const structure of structures) {
  162. for (let i = 0, il = registry.types.length; i < il; ++i) {
  163. const type = registry.types[i][0]
  164. const repr = this.getRepresentation(structure.transform.ref, type)
  165. if (repr) callback(repr, type, update)
  166. }
  167. }
  168. await this.plugin.runTask(state.updateTree(update, { doNotUpdateCurrent: true }))
  169. }
  170. setRepresentationParams(repr: RepresentationTransform, type: string, update: StateBuilder.Root, props: ReprProps) {
  171. const state = this.plugin.state.dataState;
  172. const structures = state.select(StateSelection.Generators.rootsOfType(PSO.Molecule.Structure))
  173. for (const structure of structures) {
  174. const s = structure.obj!.data
  175. const reprParams = this.getRepresentationParams(s.root, type, repr, props)
  176. update.to(repr).update(reprParams)
  177. }
  178. }
  179. async updateRepresentation(repr: RepresentationTransform, type: string, props: ReprProps) {
  180. const state = this.plugin.state.dataState;
  181. const update = state.build()
  182. this.setRepresentationParams(repr, type, update, props)
  183. await this.plugin.runTask(state.updateTree(update, { doNotUpdateCurrent: true }))
  184. }
  185. private _ignoreHydrogens = false
  186. get ignoreHydrogens () { return this._ignoreHydrogens }
  187. async setIgnoreHydrogens(ignoreHydrogens: boolean) {
  188. if (ignoreHydrogens === this._ignoreHydrogens) return
  189. await this.eachRepresentation((repr, type, update) => {
  190. if (repr.params && repr.params.values.type.params.ignoreHydrogens !== undefined) {
  191. const { name, params } = repr.params.values.type
  192. update.to(repr.transform.ref).update(
  193. StateTransforms.Representation.StructureRepresentation3D,
  194. props => ({ ...props, type: { name, params: { ...params, ignoreHydrogens }}})
  195. )
  196. }
  197. })
  198. this._ignoreHydrogens = ignoreHydrogens
  199. }
  200. private _quality = 'auto' as VisualQuality
  201. get quality () { return this._quality }
  202. async setQuality(quality: VisualQuality) {
  203. if (quality === this._quality) return
  204. await this.eachRepresentation((repr, type, update) => {
  205. if (repr.params && repr.params.values.type.params.quality !== undefined) {
  206. const { name, params } = repr.params.values.type
  207. update.to(repr.transform.ref).update(
  208. StateTransforms.Representation.StructureRepresentation3D,
  209. props => ({ ...props, type: { name, params: { ...params, quality }}})
  210. )
  211. }
  212. })
  213. this._quality = quality
  214. }
  215. private _interactionProps = PD.getDefaultValues(InteractionsProvider.defaultParams)
  216. get interactionProps () { return this._interactionProps }
  217. async setInteractionsProps(interactionProps: InteractionsProps) {
  218. const state = this.plugin.state.dataState;
  219. const update = state.build()
  220. const structures = state.select(StateSelection.Generators.rootsOfType(PSO.Molecule.Structure))
  221. for (const structure of structures) {
  222. const reprStructure = this.getRepresentationStructure(structure.transform.ref, 'interactions') // TODO use enum for type name
  223. if (reprStructure) {
  224. const customStructureProps = state.select(StateSelection.Generators.ofTransformer(StateTransforms.Model.CustomStructureProperties, reprStructure.transform.ref))[0]
  225. const params = PD.getDefaultValues(this.plugin.customStructureProperties.getParams(structure.obj?.data))
  226. params.autoAttach.push(InteractionsProvider.descriptor.name)
  227. params.properties[InteractionsProvider.descriptor.name] = interactionProps
  228. if (customStructureProps) {
  229. update.to(customStructureProps).update(params)
  230. } else {
  231. update.to(reprStructure)
  232. .insert(StateTransforms.Model.CustomStructureProperties, params)
  233. }
  234. }
  235. }
  236. await this.plugin.runTask(state.updateTree(update, { doNotUpdateCurrent: true }))
  237. this._interactionProps = interactionProps
  238. }
  239. constructor(private plugin: PluginContext) {
  240. }
  241. }