volume.ts 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257
  1. /**
  2. * Copyright (c) 2018-2022 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. * @author Aliaksei Chareshneu <chareshneu.tech@gmail.com>
  7. */
  8. import { StateTransforms } from '../transforms';
  9. import { DataFormatProvider, guessCifVariant } from './provider';
  10. import { PluginContext } from '../../mol-plugin/context';
  11. import { StateObjectSelector } from '../../mol-state';
  12. import { PluginStateObject } from '../objects';
  13. import { VolumeRepresentation3DHelpers } from '../transforms/representation';
  14. import { ColorNames } from '../../mol-util/color/names';
  15. import { Volume } from '../../mol-model/volume';
  16. import { createVolumeRepresentationParams } from '../helpers/volume-representation-params';
  17. import { objectForEach } from '../../mol-util/object';
  18. import { RecommendedIsoValue } from '../../mol-model-formats/volume/property';
  19. import { getContourLevelEmdb } from '../../mol-plugin/behavior/dynamic/volume-streaming/util';
  20. import { Task } from '../../mol-task';
  21. export const VolumeFormatCategory = 'Volume';
  22. type Params = { entryId?: string };
  23. async function tryObtainRecommendedIsoValue(plugin: PluginContext, volume?: Volume) {
  24. if (!volume) return;
  25. const { entryId } = volume;
  26. if (!entryId || !entryId.toLowerCase().startsWith('emd')) return;
  27. return plugin.runTask(Task.create('Try Set Recommended IsoValue', async ctx => {
  28. try {
  29. const absIsoLevel = await getContourLevelEmdb(plugin, ctx, entryId);
  30. RecommendedIsoValue.Provider.set(volume, Volume.IsoValue.absolute(absIsoLevel));
  31. } catch (e) {
  32. console.warn(e);
  33. }
  34. }));
  35. }
  36. function tryGetRecomendedIsoValue(volume: Volume) {
  37. const recommendedIsoValue = RecommendedIsoValue.Provider.get(volume);
  38. if (!recommendedIsoValue) return;
  39. if (recommendedIsoValue.kind === 'relative') return recommendedIsoValue;
  40. return Volume.adjustedIsoValue(volume, recommendedIsoValue.absoluteValue, 'absolute');
  41. }
  42. async function defaultVisuals(plugin: PluginContext, data: { volume: StateObjectSelector<PluginStateObject.Volume.Data> }) {
  43. const typeParams: { isoValue?: Volume.IsoValue } = {};
  44. const isoValue = data.volume.data && tryGetRecomendedIsoValue(data.volume.data);
  45. if (isoValue) typeParams.isoValue = isoValue;
  46. const visual = plugin.build().to(data.volume).apply(StateTransforms.Representation.VolumeRepresentation3D, createVolumeRepresentationParams(plugin, data.volume.data, {
  47. type: 'isosurface',
  48. typeParams,
  49. }));
  50. return [await visual.commit()];
  51. }
  52. export const Ccp4Provider = DataFormatProvider({
  53. label: 'CCP4/MRC/MAP',
  54. description: 'CCP4/MRC/MAP',
  55. category: VolumeFormatCategory,
  56. binaryExtensions: ['ccp4', 'mrc', 'map'],
  57. parse: async (plugin, data, params?: Params) => {
  58. const format = plugin.build()
  59. .to(data)
  60. .apply(StateTransforms.Data.ParseCcp4, {}, { state: { isGhost: true } });
  61. const volume = format.apply(StateTransforms.Volume.VolumeFromCcp4, { entryId: params?.entryId });
  62. await format.commit({ revertOnError: true });
  63. await tryObtainRecommendedIsoValue(plugin, volume.selector.data);
  64. return { format: format.selector, volume: volume.selector };
  65. },
  66. visuals: defaultVisuals
  67. });
  68. export const Dsn6Provider = DataFormatProvider({
  69. label: 'DSN6/BRIX',
  70. description: 'DSN6/BRIX',
  71. category: VolumeFormatCategory,
  72. binaryExtensions: ['dsn6', 'brix'],
  73. parse: async (plugin, data, params?: Params) => {
  74. const format = plugin.build()
  75. .to(data)
  76. .apply(StateTransforms.Data.ParseDsn6, {}, { state: { isGhost: true } });
  77. const volume = format.apply(StateTransforms.Volume.VolumeFromDsn6, { entryId: params?.entryId });
  78. await format.commit({ revertOnError: true });
  79. await tryObtainRecommendedIsoValue(plugin, volume.selector.data);
  80. return { format: format.selector, volume: volume.selector };
  81. },
  82. visuals: defaultVisuals
  83. });
  84. export const DxProvider = DataFormatProvider({
  85. label: 'DX',
  86. description: 'DX',
  87. category: VolumeFormatCategory,
  88. stringExtensions: ['dx'],
  89. binaryExtensions: ['dxbin'],
  90. parse: async (plugin, data, params?: Params) => {
  91. const format = plugin.build()
  92. .to(data)
  93. .apply(StateTransforms.Data.ParseDx, {}, { state: { isGhost: true } });
  94. const volume = format.apply(StateTransforms.Volume.VolumeFromDx, { entryId: params?.entryId });
  95. await volume.commit({ revertOnError: true });
  96. await tryObtainRecommendedIsoValue(plugin, volume.selector.data);
  97. return { volume: volume.selector };
  98. },
  99. visuals: defaultVisuals
  100. });
  101. export const CubeProvider = DataFormatProvider({
  102. label: 'Cube',
  103. description: 'Cube',
  104. category: VolumeFormatCategory,
  105. stringExtensions: ['cub', 'cube'],
  106. parse: async (plugin, data, params?: Params) => {
  107. const format = plugin.build()
  108. .to(data)
  109. .apply(StateTransforms.Data.ParseCube, {}, { state: { isGhost: true } });
  110. const volume = format.apply(StateTransforms.Volume.VolumeFromCube, { entryId: params?.entryId });
  111. const structure = format
  112. .apply(StateTransforms.Model.TrajectoryFromCube, void 0, { state: { isGhost: true } })
  113. .apply(StateTransforms.Model.ModelFromTrajectory)
  114. .apply(StateTransforms.Model.StructureFromModel);
  115. await format.commit({ revertOnError: true });
  116. await tryObtainRecommendedIsoValue(plugin, volume.selector.data);
  117. return { format: format.selector, volume: volume.selector, structure: structure.selector };
  118. },
  119. visuals: async (plugin: PluginContext, data: { volume: StateObjectSelector<PluginStateObject.Volume.Data>, structure: StateObjectSelector<PluginStateObject.Molecule.Structure> }) => {
  120. const surfaces = plugin.build();
  121. const volumeReprs: StateObjectSelector<PluginStateObject.Volume.Representation3D>[] = [];
  122. const volumeData = data.volume.cell?.obj?.data;
  123. if (volumeData && Volume.isOrbitals(volumeData)) {
  124. const volumePos = surfaces.to(data.volume).apply(StateTransforms.Representation.VolumeRepresentation3D, createVolumeRepresentationParams(plugin, volumeData, {
  125. type: 'isosurface',
  126. typeParams: { isoValue: Volume.IsoValue.relative(1), alpha: 0.4 },
  127. color: 'uniform',
  128. colorParams: { value: ColorNames.blue }
  129. }));
  130. const volumeNeg = surfaces.to(data.volume).apply(StateTransforms.Representation.VolumeRepresentation3D, createVolumeRepresentationParams(plugin, volumeData, {
  131. type: 'isosurface',
  132. typeParams: { isoValue: Volume.IsoValue.relative(-1), alpha: 0.4 },
  133. color: 'uniform',
  134. colorParams: { value: ColorNames.red }
  135. }));
  136. volumeReprs.push(volumePos.selector, volumeNeg.selector);
  137. } else {
  138. const volume = surfaces.to(data.volume).apply(StateTransforms.Representation.VolumeRepresentation3D, createVolumeRepresentationParams(plugin, volumeData, {
  139. type: 'isosurface',
  140. typeParams: { isoValue: Volume.IsoValue.relative(2), alpha: 0.4 },
  141. color: 'uniform',
  142. colorParams: { value: ColorNames.grey }
  143. }));
  144. volumeReprs.push(volume.selector);
  145. }
  146. const structure = await plugin.builders.structure.representation.applyPreset(data.structure, 'auto');
  147. await surfaces.commit();
  148. const structureReprs: StateObjectSelector<PluginStateObject.Molecule.Structure.Representation3D>[] = [];
  149. objectForEach(structure?.representations as any, (r: any) => {
  150. if (r) structureReprs.push(r);
  151. });
  152. return [...volumeReprs, ...structureReprs];
  153. }
  154. });
  155. type DsCifParams = { entryId?: string | string[] };
  156. export const DscifProvider = DataFormatProvider({
  157. label: 'DensityServer CIF',
  158. description: 'DensityServer CIF',
  159. category: VolumeFormatCategory,
  160. stringExtensions: ['cif'],
  161. binaryExtensions: ['bcif'],
  162. isApplicable: (info, data) => {
  163. return guessCifVariant(info, data) === 'dscif' ? true : false;
  164. },
  165. parse: async (plugin, data, params?: DsCifParams) => {
  166. const cifCell = await plugin.build().to(data).apply(StateTransforms.Data.ParseCif).commit();
  167. const b = plugin.build().to(cifCell);
  168. const blocks = cifCell.obj!.data.blocks;
  169. if (blocks.length === 0) throw new Error('no data blocks');
  170. const volumes: StateObjectSelector<PluginStateObject.Volume.Data>[] = [];
  171. let i = 0;
  172. for (const block of blocks) {
  173. // Skip "server" data block.
  174. if (block.header.toUpperCase() === 'SERVER') continue;
  175. const entryId = Array.isArray(params?.entryId) ? params?.entryId[i] : params?.entryId;
  176. if (block.categories['volume_data_3d_info']?.rowCount > 0) {
  177. volumes.push(b.apply(StateTransforms.Volume.VolumeFromDensityServerCif, { blockHeader: block.header, entryId }).selector);
  178. i++;
  179. }
  180. }
  181. await b.commit();
  182. for (const v of volumes) await tryObtainRecommendedIsoValue(plugin, v.data);
  183. return { volumes };
  184. },
  185. visuals: async (plugin, data: { volumes: StateObjectSelector<PluginStateObject.Volume.Data>[] }) => {
  186. const { volumes } = data;
  187. const tree = plugin.build();
  188. const visuals: StateObjectSelector<PluginStateObject.Volume.Representation3D>[] = [];
  189. if (volumes.length > 0) {
  190. const isoValue = (volumes[0].data && tryGetRecomendedIsoValue(volumes[0].data)) || Volume.IsoValue.relative(1.5);
  191. visuals[0] = tree
  192. .to(volumes[0])
  193. .apply(StateTransforms.Representation.VolumeRepresentation3D, VolumeRepresentation3DHelpers.getDefaultParamsStatic(plugin, 'isosurface', { isoValue, alpha: 1 }, 'uniform', { value: ColorNames.teal }))
  194. .selector;
  195. }
  196. if (volumes.length > 1) {
  197. const posParams = VolumeRepresentation3DHelpers.getDefaultParamsStatic(plugin, 'isosurface', { isoValue: Volume.IsoValue.relative(3), alpha: 0.3 }, 'uniform', { value: ColorNames.green });
  198. const negParams = VolumeRepresentation3DHelpers.getDefaultParamsStatic(plugin, 'isosurface', { isoValue: Volume.IsoValue.relative(-3), alpha: 0.3 }, 'uniform', { value: ColorNames.red });
  199. visuals[visuals.length] = tree.to(volumes[1]).apply(StateTransforms.Representation.VolumeRepresentation3D, posParams).selector;
  200. visuals[visuals.length] = tree.to(volumes[1]).apply(StateTransforms.Representation.VolumeRepresentation3D, negParams).selector;
  201. }
  202. await tree.commit();
  203. return visuals;
  204. }
  205. });
  206. export const BuiltInVolumeFormats = [
  207. ['ccp4', Ccp4Provider] as const,
  208. ['dsn6', Dsn6Provider] as const,
  209. ['cube', CubeProvider] as const,
  210. ['dx', DxProvider] as const,
  211. ['dscif', DscifProvider] as const,
  212. ] as const;
  213. export type BuildInVolumeFormat = (typeof BuiltInVolumeFormats)[number][0]