representation-preset.ts 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351
  1. /**
  2. * Copyright (c) 2019-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
  3. *
  4. * @author David Sehnal <david.sehnal@gmail.com>
  5. * @author Alexander Rose <alexander.rose@weirdbyte.de>
  6. */
  7. import { PresetProvider } from '../preset-provider';
  8. import { PluginStateObject } from '../../objects';
  9. import { ParamDefinition as PD } from '../../../mol-util/param-definition';
  10. import { VisualQuality, VisualQualityOptions } from '../../../mol-geo/geometry/base';
  11. import { ColorTheme } from '../../../mol-theme/color';
  12. import { Structure } from '../../../mol-model/structure';
  13. import { PluginContext } from '../../../mol-plugin/context';
  14. import { StateObjectRef, StateObjectSelector } from '../../../mol-state';
  15. import { StaticStructureComponentType } from '../../helpers/structure-component';
  16. import { StructureSelectionQueries as Q } from '../../helpers/structure-selection-query';
  17. import { PluginConfig } from '../../../mol-plugin/config';
  18. import { StructureFocusRepresentation } from '../../../mol-plugin/behavior/dynamic/selection/structure-focus-representation';
  19. import { createStructureColorThemeParams } from '../../helpers/structure-representation-params';
  20. export interface StructureRepresentationPresetProvider<P = any, S extends _Result = _Result> extends PresetProvider<PluginStateObject.Molecule.Structure, P, S> { }
  21. export function StructureRepresentationPresetProvider<P, S extends _Result>(repr: StructureRepresentationPresetProvider<P, S>) { return repr; }
  22. export namespace StructureRepresentationPresetProvider {
  23. export type Params<P extends StructureRepresentationPresetProvider> = P extends StructureRepresentationPresetProvider<infer T> ? T : never;
  24. export type State<P extends StructureRepresentationPresetProvider> = P extends StructureRepresentationPresetProvider<infer _, infer S> ? S : never;
  25. export type Result = {
  26. components?: { [name: string]: StateObjectSelector | undefined },
  27. representations?: { [name: string]: StateObjectSelector | undefined }
  28. }
  29. export const CommonParams = {
  30. ignoreHydrogens: PD.Optional(PD.Boolean(false)),
  31. quality: PD.Optional(PD.Select<VisualQuality>('auto', VisualQualityOptions)),
  32. theme: PD.Optional(PD.Group({
  33. globalName: PD.Optional(PD.Text<ColorTheme.BuiltIn>('')),
  34. carbonByChainId: PD.Optional(PD.Boolean(true)),
  35. focus: PD.Optional(PD.Group({
  36. name: PD.Optional(PD.Text<ColorTheme.BuiltIn>('')),
  37. params: PD.Optional(PD.Value<ColorTheme.BuiltInParams<ColorTheme.BuiltIn>>({} as any))
  38. }))
  39. }))
  40. };
  41. export type CommonParams = PD.ValuesFor<typeof CommonParams>
  42. export function reprBuilder(plugin: PluginContext, params: CommonParams) {
  43. const update = plugin.state.data.build();
  44. const builder = plugin.builders.structure.representation;
  45. const typeParams = {
  46. quality: plugin.managers.structure.component.state.options.visualQuality,
  47. ignoreHydrogens: !plugin.managers.structure.component.state.options.showHydrogens,
  48. };
  49. if (params.quality && params.quality !== 'auto') typeParams.quality = params.quality;
  50. if (params.ignoreHydrogens !== void 0) typeParams.ignoreHydrogens = !!params.ignoreHydrogens;
  51. const color: ColorTheme.BuiltIn | undefined = params.theme?.globalName ? params.theme?.globalName : void 0;
  52. const ballAndStickColor: ColorTheme.BuiltInParams<'element-symbol'> = typeof params.theme?.carbonByChainId !== 'undefined' ? { carbonByChainId: !!params.theme?.carbonByChainId } : { };
  53. return { update, builder, color, typeParams, ballAndStickColor };
  54. }
  55. export function updateFocusRepr<T extends ColorTheme.BuiltIn>(plugin: PluginContext, structure: Structure, themeName: T | undefined, themeParams: ColorTheme.BuiltInParams<T> | undefined) {
  56. if (!themeName && !themeParams) return;
  57. return plugin.state.updateBehavior(StructureFocusRepresentation, p => {
  58. const c = createStructureColorThemeParams(plugin, structure, 'ball-and-stick', themeName, themeParams);
  59. p.surroundingsParams.colorTheme = c;
  60. p.targetParams.colorTheme = c;
  61. });
  62. }
  63. }
  64. type _Result = StructureRepresentationPresetProvider.Result
  65. const CommonParams = StructureRepresentationPresetProvider.CommonParams;
  66. type CommonParams = StructureRepresentationPresetProvider.CommonParams
  67. const reprBuilder = StructureRepresentationPresetProvider.reprBuilder;
  68. const updateFocusRepr = StructureRepresentationPresetProvider.updateFocusRepr;
  69. const auto = StructureRepresentationPresetProvider({
  70. id: 'preset-structure-representation-auto',
  71. display: {
  72. name: 'Automatic',
  73. description: 'Show representations based on the size of the structure. Smaller structures are shown with more detail than larger ones, ranging from atomistic display to coarse surfaces.'
  74. },
  75. params: () => CommonParams,
  76. apply(ref, params, plugin) {
  77. const structure = StateObjectRef.resolveAndCheck(plugin.state.data, ref)?.obj?.data;
  78. if (!structure) return { };
  79. const thresholds = plugin.config.get(PluginConfig.Structure.SizeThresholds) || Structure.DefaultSizeThresholds;
  80. const size = Structure.getSize(structure, thresholds);
  81. switch (size) {
  82. case Structure.Size.Gigantic:
  83. case Structure.Size.Huge:
  84. return coarseSurface.apply(ref, params, plugin);
  85. case Structure.Size.Large:
  86. return polymerCartoon.apply(ref, params, plugin);
  87. case Structure.Size.Medium:
  88. return polymerAndLigand.apply(ref, params, plugin);
  89. case Structure.Size.Small:
  90. // `showCarbohydrateSymbol: true` is nice e.g. for PDB 1aga
  91. return atomicDetail.apply(ref, { ...params, showCarbohydrateSymbol: true }, plugin);
  92. }
  93. }
  94. });
  95. const empty = StructureRepresentationPresetProvider({
  96. id: 'preset-structure-representation-empty',
  97. display: { name: 'Empty', description: 'Removes all existing representations.' },
  98. async apply(ref, params, plugin) {
  99. return { };
  100. }
  101. });
  102. const BuiltInPresetGroupName = 'Basic';
  103. const polymerAndLigand = StructureRepresentationPresetProvider({
  104. id: 'preset-structure-representation-polymer-and-ligand',
  105. display: {
  106. name: 'Polymer & Ligand', group: BuiltInPresetGroupName,
  107. description: 'Shows polymers as Cartoon, ligands as Ball & Stick, carbohydrates as 3D-SNFG and water molecules semi-transparent.'
  108. },
  109. params: () => CommonParams,
  110. async apply(ref, params, plugin) {
  111. const structureCell = StateObjectRef.resolveAndCheck(plugin.state.data, ref);
  112. if (!structureCell) return {};
  113. const components = {
  114. polymer: await presetStaticComponent(plugin, structureCell, 'polymer'),
  115. ligand: await presetStaticComponent(plugin, structureCell, 'ligand'),
  116. nonStandard: await presetStaticComponent(plugin, structureCell, 'non-standard'),
  117. branched: await presetStaticComponent(plugin, structureCell, 'branched', { label: 'Carbohydrate' }),
  118. water: await presetStaticComponent(plugin, structureCell, 'water'),
  119. ion: await presetStaticComponent(plugin, structureCell, 'ion'),
  120. lipid: await presetStaticComponent(plugin, structureCell, 'lipid'),
  121. coarse: await presetStaticComponent(plugin, structureCell, 'coarse')
  122. };
  123. const structure = structureCell.obj!.data;
  124. const cartoonProps = {
  125. sizeFactor: structure.isCoarseGrained ? 0.8 : 0.2,
  126. };
  127. // TODO make configurable
  128. const waterType = (components.water?.obj?.data?.elementCount || 0) > 50_000 ? 'line' : 'ball-and-stick';
  129. const lipidType = (components.lipid?.obj?.data?.elementCount || 0) > 20_000 ? 'line' : 'ball-and-stick';
  130. const { update, builder, typeParams, color, ballAndStickColor } = reprBuilder(plugin, params);
  131. const representations = {
  132. polymer: builder.buildRepresentation(update, components.polymer, { type: 'cartoon', typeParams: { ...typeParams, ...cartoonProps }, color }, { tag: 'polymer' }),
  133. ligand: builder.buildRepresentation(update, components.ligand, { type: 'ball-and-stick', typeParams, color, colorParams: ballAndStickColor }, { tag: 'ligand' }),
  134. nonStandard: builder.buildRepresentation(update, components.nonStandard, { type: 'ball-and-stick', typeParams, color, colorParams: ballAndStickColor }, { tag: 'non-standard' }),
  135. branchedBallAndStick: builder.buildRepresentation(update, components.branched, { type: 'ball-and-stick', typeParams: { ...typeParams, alpha: 0.3 }, color, colorParams: ballAndStickColor }, { tag: 'branched-ball-and-stick' }),
  136. branchedSnfg3d: builder.buildRepresentation(update, components.branched, { type: 'carbohydrate', typeParams, color }, { tag: 'branched-snfg-3d' }),
  137. water: builder.buildRepresentation(update, components.water, { type: waterType, typeParams: { ...typeParams, alpha: 0.6 }, color }, { tag: 'water' }),
  138. ion: builder.buildRepresentation(update, components.ion, { type: 'ball-and-stick', typeParams, color, colorParams: { carbonByChainId: false } }, { tag: 'ion' }),
  139. lipid: builder.buildRepresentation(update, components.lipid, { type: lipidType, typeParams: { ...typeParams, alpha: 0.6 }, color, colorParams: { carbonByChainId: false } }, { tag: 'lipid' }),
  140. coarse: builder.buildRepresentation(update, components.coarse, { type: 'spacefill', typeParams, color: color || 'chain-id' }, { tag: 'coarse' })
  141. };
  142. await update.commit({ revertOnError: false });
  143. await updateFocusRepr(plugin, structure, params.theme?.focus?.name, params.theme?.focus?.params);
  144. return { components, representations };
  145. }
  146. });
  147. const proteinAndNucleic = StructureRepresentationPresetProvider({
  148. id: 'preset-structure-representation-protein-and-nucleic',
  149. display: {
  150. name: 'Protein & Nucleic', group: BuiltInPresetGroupName,
  151. description: 'Shows proteins as Cartoon and RNA/DNA as Gaussian Surface.'
  152. },
  153. params: () => CommonParams,
  154. async apply(ref, params, plugin) {
  155. const structureCell = StateObjectRef.resolveAndCheck(plugin.state.data, ref);
  156. if (!structureCell) return {};
  157. const components = {
  158. protein: await presetSelectionComponent(plugin, structureCell, 'protein'),
  159. nucleic: await presetSelectionComponent(plugin, structureCell, 'nucleic'),
  160. };
  161. const structure = structureCell.obj!.data;
  162. const cartoonProps = {
  163. sizeFactor: structure.isCoarseGrained ? 0.8 : 0.2,
  164. };
  165. const gaussianProps = {
  166. radiusOffset: structure.isCoarseGrained ? 2 : 0,
  167. smoothness: structure.isCoarseGrained ? 0.5 : 1.5,
  168. };
  169. const { update, builder, typeParams, color } = reprBuilder(plugin, params);
  170. const representations = {
  171. protein: builder.buildRepresentation(update, components.protein, { type: 'cartoon', typeParams: { ...typeParams, ...cartoonProps }, color }, { tag: 'protein' }),
  172. nucleic: builder.buildRepresentation(update, components.nucleic, { type: 'gaussian-surface', typeParams: { ...typeParams, ...gaussianProps }, color }, { tag: 'nucleic' })
  173. };
  174. await update.commit({ revertOnError: true });
  175. await updateFocusRepr(plugin, structure, params.theme?.focus?.name, params.theme?.focus?.params);
  176. return { components, representations };
  177. }
  178. });
  179. const coarseSurface = StructureRepresentationPresetProvider({
  180. id: 'preset-structure-representation-coarse-surface',
  181. display: {
  182. name: 'Coarse Surface', group: BuiltInPresetGroupName,
  183. description: 'Shows polymers as coarse Gaussian Surface.'
  184. },
  185. params: () => CommonParams,
  186. async apply(ref, params, plugin) {
  187. const structureCell = StateObjectRef.resolveAndCheck(plugin.state.data, ref);
  188. if (!structureCell) return {};
  189. const components = {
  190. polymer: await presetStaticComponent(plugin, structureCell, 'polymer')
  191. };
  192. const structure = structureCell.obj!.data;
  193. const size = Structure.getSize(structure);
  194. const gaussianProps = Object.create(null);
  195. if (size === Structure.Size.Gigantic) {
  196. Object.assign(gaussianProps, {
  197. traceOnly: true,
  198. radiusOffset: 2,
  199. smoothness: 0.5,
  200. visuals: ['structure-gaussian-surface-mesh']
  201. });
  202. } else if(size === Structure.Size.Huge) {
  203. Object.assign(gaussianProps, {
  204. radiusOffset: structure.isCoarseGrained ? 2 : 0,
  205. smoothness: 0.5,
  206. });
  207. } else if(structure.isCoarseGrained) {
  208. Object.assign(gaussianProps, {
  209. radiusOffset: 2,
  210. smoothness: 0.5,
  211. });
  212. }
  213. const { update, builder, typeParams, color } = reprBuilder(plugin, params);
  214. const representations = {
  215. polymer: builder.buildRepresentation(update, components.polymer, { type: 'gaussian-surface', typeParams: { ...typeParams, ...gaussianProps }, color }, { tag: 'polymer' })
  216. };
  217. await update.commit({ revertOnError: true });
  218. await updateFocusRepr(plugin, structure, params.theme?.focus?.name, params.theme?.focus?.params);
  219. return { components, representations };
  220. }
  221. });
  222. const polymerCartoon = StructureRepresentationPresetProvider({
  223. id: 'preset-structure-representation-polymer-cartoon',
  224. display: {
  225. name: 'Polymer Cartoon', group: BuiltInPresetGroupName,
  226. description: 'Shows polymers as Cartoon.'
  227. },
  228. params: () => CommonParams,
  229. async apply(ref, params, plugin) {
  230. const structureCell = StateObjectRef.resolveAndCheck(plugin.state.data, ref);
  231. if (!structureCell) return {};
  232. const components = {
  233. polymer: await presetStaticComponent(plugin, structureCell, 'polymer'),
  234. };
  235. const structure = structureCell.obj!.data;
  236. const cartoonProps = {
  237. sizeFactor: structure.isCoarseGrained ? 0.8 : 0.2
  238. };
  239. const { update, builder, typeParams, color } = reprBuilder(plugin, params);
  240. const representations = {
  241. polymer: builder.buildRepresentation(update, components.polymer, { type: 'cartoon', typeParams: { ...typeParams, ...cartoonProps }, color }, { tag: 'polymer' })
  242. };
  243. await update.commit({ revertOnError: true });
  244. await updateFocusRepr(plugin, structure, params.theme?.focus?.name, params.theme?.focus?.params);
  245. return { components, representations };
  246. }
  247. });
  248. const atomicDetail = StructureRepresentationPresetProvider({
  249. id: 'preset-structure-representation-atomic-detail',
  250. display: {
  251. name: 'Atomic Detail', group: BuiltInPresetGroupName,
  252. description: 'Shows everything in atomic detail with Ball & Stick.'
  253. },
  254. params: () => ({
  255. ...CommonParams,
  256. showCarbohydrateSymbol: PD.Boolean(false)
  257. }),
  258. async apply(ref, params, plugin) {
  259. const structureCell = StateObjectRef.resolveAndCheck(plugin.state.data, ref);
  260. if (!structureCell) return {};
  261. const components = {
  262. all: await presetStaticComponent(plugin, structureCell, 'all'),
  263. branched: undefined
  264. };
  265. const structure = structureCell.obj!.data;
  266. const highElementCount = structure.elementCount > 200_000; // TODO make configurable
  267. const atomicType = highElementCount ? 'line' : 'ball-and-stick';
  268. const showCarbohydrateSymbol = params.showCarbohydrateSymbol && !highElementCount;
  269. if (showCarbohydrateSymbol) {
  270. Object.assign(components, {
  271. branched: await presetStaticComponent(plugin, structureCell, 'branched', { label: 'Carbohydrate' }),
  272. });
  273. }
  274. const { update, builder, typeParams, color, ballAndStickColor } = reprBuilder(plugin, params);
  275. const representations = {
  276. all: builder.buildRepresentation(update, components.all, { type: atomicType, typeParams, color, colorParams: ballAndStickColor }, { tag: 'all' }),
  277. };
  278. if (showCarbohydrateSymbol) {
  279. Object.assign(representations, {
  280. snfg3d: builder.buildRepresentation(update, components.branched, { type: 'carbohydrate', typeParams: { ...typeParams, alpha: 0.4, visuals: ['carbohydrate-symbol'] }, color }, { tag: 'snfg-3d' }),
  281. });
  282. }
  283. await update.commit({ revertOnError: true });
  284. return { components, representations };
  285. }
  286. });
  287. export function presetStaticComponent(plugin: PluginContext, structure: StateObjectRef<PluginStateObject.Molecule.Structure>, type: StaticStructureComponentType, params?: { label?: string, tags?: string[] }) {
  288. return plugin.builders.structure.tryCreateComponentStatic(structure, type, params);
  289. }
  290. export function presetSelectionComponent(plugin: PluginContext, structure: StateObjectRef<PluginStateObject.Molecule.Structure>, query: keyof typeof Q, params?: { label?: string, tags?: string[] }) {
  291. return plugin.builders.structure.tryCreateComponentFromSelection(structure, Q[query], `selection-${query}`, params);
  292. }
  293. export const PresetStructureRepresentations = {
  294. empty,
  295. auto,
  296. 'atomic-detail': atomicDetail,
  297. 'polymer-cartoon': polymerCartoon,
  298. 'polymer-and-ligand': polymerAndLigand,
  299. 'protein-and-nucleic': proteinAndNucleic,
  300. 'coarse-surface': coarseSurface
  301. };
  302. export type PresetStructureRepresentations = typeof PresetStructureRepresentations;