volume.ts 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231
  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. */
  7. import { CIF } from '../../mol-io/reader/cif';
  8. import { Vec3 } from '../../mol-math/linear-algebra';
  9. import { volumeFromCcp4 } from '../../mol-model-formats/volume/ccp4';
  10. import { volumeFromDensityServerData } from '../../mol-model-formats/volume/density-server';
  11. import { volumeFromDsn6 } from '../../mol-model-formats/volume/dsn6';
  12. import { Task } from '../../mol-task';
  13. import { ParamDefinition as PD } from '../../mol-util/param-definition';
  14. import { PluginStateObject as SO, PluginStateTransform } from '../objects';
  15. import { volumeFromCube } from '../../mol-model-formats/volume/cube';
  16. import { volumeFromDx } from '../../mol-model-formats/volume/dx';
  17. import { Volume } from '../../mol-model/volume';
  18. import { PluginContext } from '../../mol-plugin/context';
  19. import { StateSelection } from '../../mol-state';
  20. import { volumeFromSegmentationData } from '../../mol-model-formats/volume/segmentation';
  21. export { VolumeFromCcp4 };
  22. export { VolumeFromDsn6 };
  23. export { VolumeFromCube };
  24. export { VolumeFromDx };
  25. export { AssignColorVolume };
  26. export { VolumeFromDensityServerCif };
  27. export { VolumeFromSegmentationCif };
  28. type VolumeFromCcp4 = typeof VolumeFromCcp4
  29. const VolumeFromCcp4 = PluginStateTransform.BuiltIn({
  30. name: 'volume-from-ccp4',
  31. display: { name: 'Volume from CCP4/MRC/MAP', description: 'Create Volume from CCP4/MRC/MAP data' },
  32. from: SO.Format.Ccp4,
  33. to: SO.Volume.Data,
  34. params(a) {
  35. return {
  36. voxelSize: PD.Vec3(Vec3.create(1, 1, 1)),
  37. offset: PD.Vec3(Vec3.create(0, 0, 0)),
  38. entryId: PD.Text(''),
  39. };
  40. }
  41. })({
  42. apply({ a, params }) {
  43. return Task.create('Create volume from CCP4/MRC/MAP', async ctx => {
  44. const volume = await volumeFromCcp4(a.data, { ...params, label: a.data.name || a.label }).runInContext(ctx);
  45. const props = { label: volume.label || 'Volume', description: `Volume ${a.data.header.NX}\u00D7${a.data.header.NX}\u00D7${a.data.header.NX}` };
  46. return new SO.Volume.Data(volume, props);
  47. });
  48. },
  49. dispose({ b }) {
  50. b?.data.customProperties.dispose();
  51. }
  52. });
  53. type VolumeFromDsn6 = typeof VolumeFromDsn6
  54. const VolumeFromDsn6 = PluginStateTransform.BuiltIn({
  55. name: 'volume-from-dsn6',
  56. display: { name: 'Volume from DSN6/BRIX', description: 'Create Volume from DSN6/BRIX data' },
  57. from: SO.Format.Dsn6,
  58. to: SO.Volume.Data,
  59. params(a) {
  60. return {
  61. voxelSize: PD.Vec3(Vec3.create(1, 1, 1)),
  62. entryId: PD.Text(''),
  63. };
  64. }
  65. })({
  66. apply({ a, params }) {
  67. return Task.create('Create volume from DSN6/BRIX', async ctx => {
  68. const volume = await volumeFromDsn6(a.data, { ...params, label: a.data.name || a.label }).runInContext(ctx);
  69. const props = { label: volume.label || 'Volume', description: `Volume ${a.data.header.xExtent}\u00D7${a.data.header.yExtent}\u00D7${a.data.header.zExtent}` };
  70. return new SO.Volume.Data(volume, props);
  71. });
  72. },
  73. dispose({ b }) {
  74. b?.data.customProperties.dispose();
  75. }
  76. });
  77. type VolumeFromCube = typeof VolumeFromCube
  78. const VolumeFromCube = PluginStateTransform.BuiltIn({
  79. name: 'volume-from-cube',
  80. display: { name: 'Volume from Cube', description: 'Create Volume from Cube data' },
  81. from: SO.Format.Cube,
  82. to: SO.Volume.Data,
  83. params(a) {
  84. const dataIndex = a ? PD.Select(0, a.data.header.dataSetIds.map((id, i) => [i, `${id}`] as const)) : PD.Numeric(0);
  85. return {
  86. dataIndex,
  87. entryId: PD.Text(''),
  88. };
  89. }
  90. })({
  91. apply({ a, params }) {
  92. return Task.create('Create volume from Cube', async ctx => {
  93. const volume = await volumeFromCube(a.data, { ...params, label: a.data.name || a.label }).runInContext(ctx);
  94. const props = { label: volume.label || 'Volume', description: `Volume ${a.data.header.dim[0]}\u00D7${a.data.header.dim[1]}\u00D7${a.data.header.dim[2]}` };
  95. return new SO.Volume.Data(volume, props);
  96. });
  97. },
  98. dispose({ b }) {
  99. b?.data.customProperties.dispose();
  100. }
  101. });
  102. type VolumeFromDx = typeof VolumeFromDx
  103. const VolumeFromDx = PluginStateTransform.BuiltIn({
  104. name: 'volume-from-dx',
  105. display: { name: 'Parse DX', description: 'Create volume from DX data.' },
  106. from: SO.Format.Dx,
  107. to: SO.Volume.Data
  108. })({
  109. apply({ a }) {
  110. return Task.create('Parse DX', async ctx => {
  111. const volume = await volumeFromDx(a.data, { label: a.data.name || a.label }).runInContext(ctx);
  112. const props = { label: volume.label || 'Volume', description: `Volume ${a.data.header.dim[0]}\u00D7${a.data.header.dim[1]}\u00D7${a.data.header.dim[2]}` };
  113. return new SO.Volume.Data(volume, props);
  114. });
  115. },
  116. dispose({ b }) {
  117. b?.data.customProperties.dispose();
  118. }
  119. });
  120. type VolumeFromDensityServerCif = typeof VolumeFromDensityServerCif
  121. const VolumeFromDensityServerCif = PluginStateTransform.BuiltIn({
  122. name: 'volume-from-density-server-cif',
  123. display: { name: 'Volume from density-server CIF', description: 'Identify and create all separate models in the specified CIF data block' },
  124. from: SO.Format.Cif,
  125. to: SO.Volume.Data,
  126. params(a) {
  127. if (!a) {
  128. return {
  129. blockHeader: PD.Optional(PD.Text(void 0, { description: 'Header of the block to parse. If none is specifed, the 1st data block in the file is used.' })),
  130. entryId: PD.Text(''),
  131. };
  132. }
  133. const blocks = a.data.blocks.slice(1); // zero block contains query meta-data
  134. return {
  135. blockHeader: PD.Optional(PD.Select(blocks[0] && blocks[0].header, blocks.map(b => [b.header, b.header] as [string, string]), { description: 'Header of the block to parse' })),
  136. entryId: PD.Text(''),
  137. };
  138. }
  139. })({
  140. isApplicable: a => a.data.blocks.length > 0,
  141. apply({ a, params }) {
  142. return Task.create('Parse density-server CIF', async ctx => {
  143. const header = params.blockHeader || a.data.blocks[1].header; // zero block contains query meta-data
  144. const block = a.data.blocks.find(b => b.header === header);
  145. if (!block) throw new Error(`Data block '${[header]}' not found.`);
  146. const densityServerCif = CIF.schema.densityServer(block);
  147. const volume = await volumeFromDensityServerData(densityServerCif, { entryId: params.entryId }).runInContext(ctx);
  148. const [x, y, z] = volume.grid.cells.space.dimensions;
  149. const props = { label: params.entryId ?? densityServerCif.volume_data_3d_info.name.value(0), description: `Volume ${x}\u00D7${y}\u00D7${z}` };
  150. return new SO.Volume.Data(volume, props);
  151. });
  152. },
  153. dispose({ b }) {
  154. b?.data.customProperties.dispose();
  155. }
  156. });
  157. type VolumeFromSegmentationCif = typeof VolumeFromSegmentationCif
  158. const VolumeFromSegmentationCif = PluginStateTransform.BuiltIn({
  159. name: 'volume-from-segmentation-cif',
  160. display: { name: 'Volume from Segmentation CIF' },
  161. from: SO.Format.Cif,
  162. to: SO.Volume.Data,
  163. params(a) {
  164. const blocks = a?.data.blocks.slice(1);
  165. const blockHeaderParam = blocks ?
  166. PD.Optional(PD.Select(blocks[0] && blocks[0].header, blocks.map(b => [b.header, b.header] as [string, string]), { description: 'Header of the block to parse' }))
  167. : PD.Optional(PD.Text(void 0, { description: 'Header of the block to parse. If none is specifed, the 1st data block in the file is used.' }));
  168. return {
  169. blockHeader: blockHeaderParam,
  170. segmentLabels: PD.ObjectList({ id: PD.Numeric(-1), label: PD.Text('') }, s => `${s.id} = ${s.label}`, { description: 'Mapping of segment IDs to segment labels' }),
  171. ownerId: PD.Text('', { isHidden: true, description: 'Reference to the object which manages this volume' }),
  172. };
  173. }
  174. })({
  175. isApplicable: a => a.data.blocks.length > 0,
  176. apply({ a, params }) {
  177. return Task.create('Parse segmentation CIF', async ctx => {
  178. const header = params.blockHeader || a.data.blocks[1].header; // zero block contains query meta-data
  179. const block = a.data.blocks.find(b => b.header === header);
  180. if (!block) throw new Error(`Data block '${[header]}' not found.`);
  181. const segmentationCif = CIF.schema.segmentation(block);
  182. const segmentLabels: { [id: number]: string } = {};
  183. for (const segment of params.segmentLabels) segmentLabels[segment.id] = segment.label;
  184. const volume = await volumeFromSegmentationData(segmentationCif, { segmentLabels, ownerId: params.ownerId }).runInContext(ctx);
  185. const [x, y, z] = volume.grid.cells.space.dimensions;
  186. const props = { label: segmentationCif.volume_data_3d_info.name.value(0), description: `Segmentation ${x}\u00D7${y}\u00D7${z}` };
  187. return new SO.Volume.Data(volume, props);
  188. });
  189. },
  190. dispose({ b }) {
  191. b?.data.customProperties.dispose();
  192. }
  193. });
  194. type AssignColorVolume = typeof AssignColorVolume
  195. const AssignColorVolume = PluginStateTransform.BuiltIn({
  196. name: 'assign-color-volume',
  197. display: { name: 'Assign Color Volume', description: 'Assigns another volume to be available for coloring.' },
  198. from: SO.Volume.Data,
  199. to: SO.Volume.Data,
  200. isDecorator: true,
  201. params(a, plugin: PluginContext) {
  202. if (!a) return { ref: PD.Text() };
  203. const cells = plugin.state.data.select(StateSelection.Generators.root.subtree().ofType(SO.Volume.Data).filter(cell => !!cell.obj && !cell.obj?.data.colorVolume && cell.obj !== a));
  204. if (cells.length === 0) return { ref: PD.Text('', { isHidden: true }) };
  205. return { ref: PD.Select(cells[0].transform.ref, cells.map(c => [c.transform.ref, c.obj!.label])) };
  206. }
  207. })({
  208. apply({ a, params, dependencies }) {
  209. return Task.create('Assign Color Volume', async ctx => {
  210. if (!dependencies || !dependencies[params.ref]) {
  211. throw new Error('Dependency not available.');
  212. }
  213. const colorVolume = dependencies[params.ref].data as Volume;
  214. const volume: Volume = {
  215. ...a.data,
  216. colorVolume
  217. };
  218. const props = { label: a.label, description: 'Volume + Colors' };
  219. return new SO.Volume.Data(volume, props);
  220. });
  221. }
  222. });