transformers.ts 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  1. /**
  2. * Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
  3. *
  4. * @author David Sehnal <david.sehnal@gmail.com>
  5. */
  6. import { PluginStateObject as SO, PluginStateTransform } from '../../../state/objects';
  7. import { VolumeServerInfo, VolumeServerHeader } from './model';
  8. import { ParamDefinition as PD } from 'mol-util/param-definition';
  9. import { Task } from 'mol-task';
  10. import { PluginContext } from 'mol-plugin/context';
  11. import { urlCombine } from 'mol-util/url';
  12. import { createIsoValueParam } from 'mol-repr/volume/isosurface';
  13. import { VolumeIsoValue } from 'mol-model/volume';
  14. import { StateAction, StateObject, StateTransformer } from 'mol-state';
  15. import { getStreamingMethod, getEmdbIdAndContourLevel } from './util';
  16. import { VolumeStreaming } from './behavior';
  17. import { VolumeRepresentation3DHelpers } from 'mol-plugin/state/transforms/representation';
  18. import { BuiltInVolumeRepresentations } from 'mol-repr/volume/registry';
  19. import { createTheme } from 'mol-theme/theme';
  20. // import { PluginContext } from 'mol-plugin/context';
  21. export const InitVolumeStreaming = StateAction.build({
  22. display: { name: 'Volume Streaming' },
  23. from: SO.Molecule.Structure,
  24. params(a) {
  25. return {
  26. method: PD.Select<VolumeServerInfo.Kind>(getStreamingMethod(a && a.data), [['em', 'EM'], ['x-ray', 'X-Ray']]),
  27. id: PD.Text((a && a.data.models[0].label) || ''),
  28. serverUrl: PD.Text('https://webchem.ncbr.muni.cz/DensityServer')
  29. };
  30. }
  31. })(({ ref, state, params }, plugin: PluginContext) => Task.create('Volume Streaming', async taskCtx => {
  32. // TODO: custom react view for this and the VolumeStreamingBehavior transformer
  33. let dataId = params.id.toLowerCase(), emDefaultContourLevel: number | undefined;
  34. if (params.method === 'em') {
  35. await taskCtx.update('Getting EMDB info...');
  36. const emInfo = await getEmdbIdAndContourLevel(plugin, taskCtx, dataId);
  37. dataId = emInfo.emdbId;
  38. emDefaultContourLevel = emInfo.contour;
  39. }
  40. const infoTree = state.build().to(ref)
  41. .apply(CreateVolumeStreamingInfo, {
  42. serverUrl: params.serverUrl,
  43. source: params.method === 'em'
  44. ? { name: 'em', params: { isoValue: VolumeIsoValue.absolute(emDefaultContourLevel || 0) } }
  45. : { name: 'x-ray', params: { } },
  46. dataId
  47. });
  48. const infoObj = await state.updateTree(infoTree).runInContext(taskCtx);
  49. const behTree = state.build().to(infoTree.ref).apply(CreateVolumeStreamingBehavior, PD.getDefaultValues(VolumeStreaming.createParams(infoObj.data)));
  50. if (params.method === 'em') {
  51. behTree.apply(VolumeStreamingVisual, { channel: 'em' }, { props: { isGhost: true } });
  52. } else {
  53. behTree.apply(VolumeStreamingVisual, { channel: '2fo-fc' }, { props: { isGhost: true } });
  54. behTree.apply(VolumeStreamingVisual, { channel: 'fo-fc(+ve)' }, { props: { isGhost: true } });
  55. behTree.apply(VolumeStreamingVisual, { channel: 'fo-fc(-ve)' }, { props: { isGhost: true } });
  56. }
  57. await state.updateTree(behTree).runInContext(taskCtx);
  58. }));
  59. export { CreateVolumeStreamingInfo }
  60. type CreateVolumeStreamingInfo = typeof CreateVolumeStreamingInfo
  61. const CreateVolumeStreamingInfo = PluginStateTransform.BuiltIn({
  62. name: 'create-volume-streaming-info',
  63. display: { name: 'Volume Streaming Info' },
  64. from: SO.Molecule.Structure,
  65. to: VolumeServerInfo,
  66. params(a) {
  67. return {
  68. serverUrl: PD.Text('https://webchem.ncbr.muni.cz/DensityServer'),
  69. source: PD.MappedStatic('x-ray', {
  70. 'em': PD.Group({
  71. isoValue: createIsoValueParam(VolumeIsoValue.relative(1))
  72. }),
  73. 'x-ray': PD.Group({ })
  74. }),
  75. dataId: PD.Text('')
  76. };
  77. }
  78. })({
  79. apply: ({ a, params }, plugin: PluginContext) => Task.create('', async taskCtx => {
  80. const dataId = params.dataId;
  81. const emDefaultContourLevel = params.source.name === 'em' ? params.source.params.isoValue : VolumeIsoValue.relative(1);
  82. await taskCtx.update('Getting server header...');
  83. const header = await plugin.fetch<VolumeServerHeader>({ url: urlCombine(params.serverUrl, `${params.source.name}/${dataId}`), type: 'json' }).runInContext(taskCtx);
  84. const data: VolumeServerInfo.Data = {
  85. serverUrl: params.serverUrl,
  86. dataId,
  87. kind: params.source.name,
  88. header,
  89. emDefaultContourLevel,
  90. structure: a.data
  91. };
  92. return new VolumeServerInfo(data, { label: `Volume Streaming: ${dataId}` });
  93. })
  94. });
  95. export { CreateVolumeStreamingBehavior }
  96. type CreateVolumeStreamingBehavior = typeof CreateVolumeStreamingBehavior
  97. const CreateVolumeStreamingBehavior = PluginStateTransform.BuiltIn({
  98. name: 'create-volume-streaming-behavior',
  99. display: { name: 'Volume Streaming Behavior' },
  100. from: VolumeServerInfo,
  101. to: VolumeStreaming,
  102. params(a) {
  103. return VolumeStreaming.createParams(a && a.data);
  104. }
  105. })({
  106. canAutoUpdate: ({ oldParams, newParams }) => {
  107. return oldParams.view === newParams.view
  108. || (oldParams.view.name === newParams.view.name && oldParams.view.name === 'selection-box');
  109. },
  110. apply: ({ a, params }, plugin: PluginContext) => Task.create('Volume streaming', async _ => {
  111. const behavior = new VolumeStreaming.Behavior(plugin, a.data);
  112. await behavior.update(params);
  113. return new VolumeStreaming(behavior, { label: 'Streaming Controls' });
  114. }),
  115. update({ b, newParams }) {
  116. return Task.create('Update Volume Streaming', async _ => {
  117. return await b.data.update(newParams) ? StateTransformer.UpdateResult.Updated : StateTransformer.UpdateResult.Unchanged;
  118. });
  119. }
  120. });
  121. export { VolumeStreamingVisual }
  122. type VolumeStreamingVisual = typeof VolumeStreamingVisual
  123. const VolumeStreamingVisual = PluginStateTransform.BuiltIn({
  124. name: 'create-volume-streaming-visual',
  125. display: { name: 'Volume Streaming Visual' },
  126. from: VolumeStreaming,
  127. to: SO.Volume.Representation3D,
  128. params: {
  129. channel: PD.Select<VolumeStreaming.ChannelType>('em', VolumeStreaming.ChannelTypeOptions, { isHidden: true })
  130. }
  131. })({
  132. apply: ({ a, params: srcParams }, plugin: PluginContext) => Task.create('Volume Representation', async ctx => {
  133. const channel = a.data.channels[srcParams.channel];
  134. if (!channel) return StateObject.Null;
  135. const params = createVolumeProps(a.data, srcParams.channel);
  136. const provider = BuiltInVolumeRepresentations.isosurface;
  137. const props = params.type.params || {}
  138. const repr = provider.factory({ webgl: plugin.canvas3d.webgl, ...plugin.volumeRepresentation.themeCtx }, provider.getParams)
  139. repr.setTheme(createTheme(plugin.volumeRepresentation.themeCtx, { volume: channel.data }, params))
  140. await repr.createOrUpdate(props, channel.data).runInContext(ctx);
  141. return new SO.Volume.Representation3D(repr, { label: `${Math.round(channel.isoValue.relativeValue * 100) / 100} σ [${srcParams.channel}]` });
  142. }),
  143. update: ({ a, b, oldParams, newParams }, plugin: PluginContext) => Task.create('Volume Representation', async ctx => {
  144. // TODO : check if params/underlying data/etc have changed; maybe will need to export "data" or some other "tag" in the Representation for this to work
  145. const channel = a.data.channels[newParams.channel];
  146. // TODO: is this correct behavior?
  147. if (!channel) return StateTransformer.UpdateResult.Unchanged;
  148. const params = createVolumeProps(a.data, newParams.channel);
  149. const props = { ...b.data.props, ...params.type.params };
  150. b.data.setTheme(createTheme(plugin.volumeRepresentation.themeCtx, { volume: channel.data }, params))
  151. await b.data.createOrUpdate(props, channel.data).runInContext(ctx);
  152. return StateTransformer.UpdateResult.Updated;
  153. })
  154. });
  155. function createVolumeProps(streaming: VolumeStreaming.Behavior, channelName: VolumeStreaming.ChannelType) {
  156. const channel = streaming.channels[channelName]!;
  157. return VolumeRepresentation3DHelpers.getDefaultParamsStatic(streaming.plugin, 'isosurface',
  158. { isoValue: channel.isoValue, alpha: channel.opacity }, 'uniform', { value: channel.color });
  159. }