entry-segmentation.ts 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131
  1. /**
  2. * Copyright (c) 2018-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
  3. *
  4. * @author Adam Midlik <midlik@gmail.com>
  5. */
  6. import { Volume } from '../../mol-model/volume';
  7. import { createVolumeRepresentationParams } from '../../mol-plugin-state/helpers/volume-representation-params';
  8. import { StateTransforms } from '../../mol-plugin-state/transforms';
  9. import { Download, ParseCif } from '../../mol-plugin-state/transforms/data';
  10. import { CreateGroup } from '../../mol-plugin-state/transforms/misc';
  11. import { VolumeFromSegmentationCif } from '../../mol-plugin-state/transforms/volume';
  12. import { PluginCommands } from '../../mol-plugin/commands';
  13. import { Color } from '../../mol-util/color';
  14. import { Segment } from './volseg-api/data';
  15. import { BOX, VolsegEntryData, MAX_VOXELS } from './entry-root';
  16. import { VolumeVisualParams } from './entry-volume';
  17. import { VolsegGlobalStateData } from './global-state';
  18. const GROUP_TAG = 'lattice-segmentation-group';
  19. const SEGMENT_VISUAL_TAG = 'lattice-segment-visual';
  20. const DEFAULT_SEGMENT_COLOR = Color.fromNormalizedRgb(0.8, 0.8, 0.8);
  21. export class VolsegLatticeSegmentationData {
  22. private entryData: VolsegEntryData;
  23. constructor(rootData: VolsegEntryData) {
  24. this.entryData = rootData;
  25. }
  26. async loadSegmentation() {
  27. const hasLattices = this.entryData.metadata.raw.grid.segmentation_lattices.segmentation_lattice_ids.length > 0;
  28. if (hasLattices) {
  29. const url = this.entryData.api.latticeUrl(this.entryData.source, this.entryData.entryId, 0, BOX, MAX_VOXELS);
  30. let group = this.entryData.findNodesByTags(GROUP_TAG)[0]?.transform.ref;
  31. if (!group) {
  32. const newGroupNode = await this.entryData.newUpdate().apply(CreateGroup,
  33. { label: 'Segmentation', description: 'Lattice' }, { tags: [GROUP_TAG], state: { isCollapsed: true } }).commit();
  34. group = newGroupNode.ref;
  35. }
  36. const segmentLabels = this.entryData.metadata.allSegments.map(seg => ({ id: seg.id, label: seg.biological_annotation.name ? `<b>${seg.biological_annotation.name}</b>` : '' }));
  37. const volumeNode = await this.entryData.newUpdate().to(group)
  38. .apply(Download, { url, isBinary: true, label: `Segmentation Data: ${url}` })
  39. .apply(ParseCif)
  40. .apply(VolumeFromSegmentationCif, { blockHeader: 'SEGMENTATION_DATA', segmentLabels: segmentLabels, ownerId: this.entryData.ref })
  41. .commit();
  42. const volumeData = volumeNode.data as Volume;
  43. const segmentation = Volume.Segmentation.get(volumeData);
  44. const segmentIds: number[] = Array.from(segmentation?.segments.keys() ?? []);
  45. await this.entryData.newUpdate().to(volumeNode)
  46. .apply(StateTransforms.Representation.VolumeRepresentation3D, createVolumeRepresentationParams(this.entryData.plugin, volumeData, {
  47. type: 'segment',
  48. typeParams: { tryUseGpu: VolsegGlobalStateData.getGlobalState(this.entryData.plugin)?.tryUseGpu },
  49. color: 'volume-segment',
  50. colorParams: { palette: this.createPalette(segmentIds) },
  51. }), { tags: [SEGMENT_VISUAL_TAG] }).commit();
  52. }
  53. }
  54. private createPalette(segmentIds: number[]) {
  55. const colorMap = new Map<number, Color>();
  56. for (const segment of this.entryData.metadata.allSegments) {
  57. const color = Color.fromNormalizedArray(segment.colour, 0);
  58. colorMap.set(segment.id, color);
  59. }
  60. if (colorMap.size === 0) return undefined;
  61. for (const segid of segmentIds) {
  62. colorMap.get(segid);
  63. }
  64. const colors = segmentIds.map(segid => colorMap.get(segid) ?? DEFAULT_SEGMENT_COLOR);
  65. return { name: 'colors' as const, params: { list: { kind: 'set' as const, colors: colors } } };
  66. }
  67. async updateOpacity(opacity: number) {
  68. const reprs = this.entryData.findNodesByTags(SEGMENT_VISUAL_TAG);
  69. const update = this.entryData.newUpdate();
  70. for (const s of reprs) {
  71. update.to(s).update(StateTransforms.Representation.VolumeRepresentation3D, p => { p.type.params.alpha = opacity; });
  72. }
  73. return await update.commit();
  74. }
  75. private makeLoci(segments: number[]) {
  76. const vis = this.entryData.findNodesByTags(SEGMENT_VISUAL_TAG)[0];
  77. if (!vis) return undefined;
  78. const repr = vis.obj?.data.repr;
  79. const wholeLoci = repr.getAllLoci()[0];
  80. if (!wholeLoci || !Volume.Segment.isLoci(wholeLoci)) return undefined;
  81. return { loci: Volume.Segment.Loci(wholeLoci.volume, segments), repr: repr };
  82. }
  83. async highlightSegment(segment: Segment) {
  84. const segmentLoci = this.makeLoci([segment.id]);
  85. if (!segmentLoci) return;
  86. this.entryData.plugin.managers.interactivity.lociHighlights.highlight(segmentLoci, false);
  87. }
  88. async selectSegment(segment?: number) {
  89. if (segment === undefined || segment < 0) return;
  90. const segmentLoci = this.makeLoci([segment]);
  91. if (!segmentLoci) return;
  92. this.entryData.plugin.managers.interactivity.lociSelects.select(segmentLoci, false);
  93. }
  94. /** Make visible the specified set of lattice segments */
  95. async showSegments(segments: number[]) {
  96. const repr = this.entryData.findNodesByTags(SEGMENT_VISUAL_TAG)[0];
  97. if (!repr) return;
  98. const selectedSegment = this.entryData.currentState.value.selectedSegment;
  99. const mustReselect = segments.includes(selectedSegment) && !repr.params?.values.type.params.segments.includes(selectedSegment);
  100. const update = this.entryData.newUpdate();
  101. update.to(repr).update(StateTransforms.Representation.VolumeRepresentation3D, p => { p.type.params.segments = segments; });
  102. await update.commit();
  103. if (mustReselect) {
  104. await this.selectSegment(this.entryData.currentState.value.selectedSegment);
  105. }
  106. }
  107. async setTryUseGpu(tryUseGpu: boolean) {
  108. const visuals = this.entryData.findNodesByTags(SEGMENT_VISUAL_TAG);
  109. for (const visual of visuals) {
  110. const oldParams: VolumeVisualParams = visual.transform.params;
  111. if (oldParams.type.params.tryUseGpu === !tryUseGpu) {
  112. const newParams = { ...oldParams, type: { ...oldParams.type, params: { ...oldParams.type.params, tryUseGpu: tryUseGpu } } };
  113. const update = this.entryData.newUpdate().to(visual.transform.ref).update(newParams);
  114. await PluginCommands.State.Update(this.entryData.plugin, { state: this.entryData.plugin.state.data, tree: update, options: { doNotUpdateCurrent: true } });
  115. }
  116. }
  117. }
  118. }