entry-meshes.ts 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103
  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 { PluginStateObject } from '../../mol-plugin-state/objects';
  7. import { CreateGroup } from '../../mol-plugin-state/transforms/misc';
  8. import { ShapeRepresentation3D } from '../../mol-plugin-state/transforms/representation';
  9. import { setSubtreeVisibility } from '../../mol-plugin/behavior/static/state';
  10. import { PluginCommands } from '../../mol-plugin/commands';
  11. import { Color } from '../../mol-util/color';
  12. import { ColorNames } from '../../mol-util/color/names';
  13. import { createMeshFromUrl } from '../meshes/examples';
  14. import { BACKGROUND_SEGMENT_VOLUME_THRESHOLD } from '../meshes/mesh-streaming/behavior';
  15. import { Segment } from './volseg-api/data';
  16. import { VolsegEntryData } from './entry-root';
  17. const DEFAULT_MESH_DETAIL: number | null = 5; // null means worst
  18. export class VolsegMeshSegmentationData {
  19. private entryData: VolsegEntryData;
  20. constructor(rootData: VolsegEntryData) {
  21. this.entryData = rootData;
  22. }
  23. async loadSegmentation() {
  24. const hasMeshes = this.entryData.metadata.meshSegmentIds.length > 0;
  25. if (hasMeshes) {
  26. await this.showSegments(this.entryData.metadata.allSegmentIds);
  27. }
  28. }
  29. updateOpacity(opacity: number) {
  30. const visuals = this.entryData.findNodesByTags('mesh-segment-visual');
  31. const update = this.entryData.newUpdate();
  32. for (const visual of visuals) {
  33. update.to(visual).update(ShapeRepresentation3D, p => { (p as any).alpha = opacity; });
  34. }
  35. return update.commit();
  36. }
  37. async highlightSegment(segment: Segment) {
  38. const visuals = this.entryData.findNodesByTags('mesh-segment-visual', `segment-${segment.id}`);
  39. for (const visual of visuals) {
  40. await PluginCommands.Interactivity.Object.Highlight(this.entryData.plugin, { state: this.entryData.plugin.state.data, ref: visual.transform.ref });
  41. }
  42. }
  43. async selectSegment(segment?: number) {
  44. if (segment === undefined || segment < 0) return;
  45. const visuals = this.entryData.findNodesByTags('mesh-segment-visual', `segment-${segment}`);
  46. const reprNode: PluginStateObject.Shape.Representation3D | undefined = visuals[0]?.obj;
  47. if (!reprNode) return;
  48. const loci = reprNode.data.repr.getAllLoci()[0];
  49. if (!loci) return;
  50. this.entryData.plugin.managers.interactivity.lociSelects.select({ loci: loci, repr: reprNode.data.repr }, false);
  51. }
  52. /** Make visible the specified set of mesh segments */
  53. async showSegments(segments: number[]) {
  54. const segmentsToShow = new Set(segments);
  55. const visuals = this.entryData.findNodesByTags('mesh-segment-visual');
  56. for (const visual of visuals) {
  57. const theTag = visual.obj?.tags?.find(tag => tag.startsWith('segment-'));
  58. if (!theTag) continue;
  59. const id = parseInt(theTag.split('-')[1]);
  60. const visibility = segmentsToShow.has(id);
  61. setSubtreeVisibility(this.entryData.plugin.state.data, visual.transform.ref, !visibility); // true means hide, ¯\_(ツ)_/¯
  62. segmentsToShow.delete(id);
  63. }
  64. const segmentsToCreate = this.entryData.metadata.meshSegmentIds.filter(seg => segmentsToShow.has(seg));
  65. if (segmentsToCreate.length === 0) return;
  66. let group = this.entryData.findNodesByTags('mesh-segmentation-group')[0]?.transform.ref;
  67. if (!group) {
  68. const newGroupNode = await this.entryData.newUpdate().apply(CreateGroup, { label: 'Segmentation', description: 'Mesh' }, { tags: ['mesh-segmentation-group'], state: { isCollapsed: true } }).commit();
  69. group = newGroupNode.ref;
  70. }
  71. const totalVolume = this.entryData.metadata.gridTotalVolume;
  72. const awaiting = [];
  73. for (const seg of segmentsToCreate) {
  74. const segment = this.entryData.metadata.getSegment(seg);
  75. if (!segment) continue;
  76. const detail = this.entryData.metadata.getSufficientMeshDetail(seg, DEFAULT_MESH_DETAIL);
  77. const color = segment.colour.length >= 3 ? Color.fromNormalizedArray(segment.colour, 0) : ColorNames.gray;
  78. const url = this.entryData.api.meshUrl_Bcif(this.entryData.source, this.entryData.entryId, seg, detail);
  79. const label = segment.biological_annotation.name ?? `Segment ${seg}`;
  80. const meshPromise = createMeshFromUrl(this.entryData.plugin, url, seg, detail, true, color, group,
  81. BACKGROUND_SEGMENT_VOLUME_THRESHOLD * totalVolume, `<b>${label}</b>`, this.entryData.ref);
  82. awaiting.push(meshPromise);
  83. }
  84. for (const promise of awaiting) await promise;
  85. }
  86. }