Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 6 additions & 13 deletions extensions/cornerstone-dicom-rt/src/getSopClassHandlerModule.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { utils, Types as OhifTypes } from '@ohif/core';
import i18n from '@ohif/i18n';
import { segmentation as cstSegmentation } from '@cornerstonejs/tools';

import { SOPClassHandlerId } from './id';
import loadRTStruct from './loadRTStruct';
Expand All @@ -8,8 +9,6 @@ const { sopClassDictionary } = utils;

const sopClassUids = [sopClassDictionary.RTStructureSetStorage];

const cachedRTStructsSEG = new Set<string>();

const loadPromises = {};

function _getDisplaySetsFromSeries(
Expand Down Expand Up @@ -143,23 +142,13 @@ function _load(
if (
(rtDisplaySet.loading || rtDisplaySet.isLoaded) &&
loadPromises[SOPInstanceUID] &&
cachedRTStructsSEG.has(rtDisplaySet.displaySetInstanceUID)
_segmentationExists(rtDisplaySet)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When an RTSTRUCT contour segmentation was deleted completed and then reloaded it wouldn't. So this check forces the reload when it no longer exists.

) {
return loadPromises[SOPInstanceUID];
}

rtDisplaySet.loading = true;

const { unsubscribe } = segmentationService.subscribe(
segmentationService.EVENTS.SEGMENTATION_LOADING_COMPLETE,
(evt: { rtDisplaySet: { displaySetInstanceUID: string } }) => {
if (evt.rtDisplaySet?.displaySetInstanceUID === rtDisplaySet.displaySetInstanceUID) {
cachedRTStructsSEG.add(rtDisplaySet.displaySetInstanceUID);
unsubscribe();
}
}
);

// We don't want to fire multiple loads, so we'll wait for the first to finish
// and also return the same promise to any other callers.
loadPromises[SOPInstanceUID] = new Promise<void>(async (resolve, reject) => {
Expand Down Expand Up @@ -219,6 +208,10 @@ function _deriveReferencedSeriesSequenceFromFrameOfReferenceSequence(
return ReferencedSeriesSequence;
}

function _segmentationExists(segDisplaySet) {
return !!cstSegmentation.state.getSegmentation(segDisplaySet.displaySetInstanceUID);
}

function getSopClassHandlerModule(params: OhifTypes.Extensions.ExtensionParams) {
const { servicesManager, extensionManager } = params;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ function OHIFCornerstoneRTViewport(props: withAppTypes) {

return () => {
// remove the segmentation representations if seg displayset changed
segmentationService.removeSegmentationRepresentations(viewportId);
segmentationService.removeRepresentationsFromViewport(viewportId);
referencedDisplaySetRef.current = null;
toolGroupService.destroyToolGroup(toolGroupId);
};
Expand Down
14 changes: 8 additions & 6 deletions extensions/cornerstone/src/commandsModule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1735,12 +1735,14 @@ function commandsModule({
* Removes a segmentation from the viewport
* @param props.segmentationId - The ID of the segmentation to remove
*/
removeSegmentationFromViewportCommand: ({ segmentationId }) => {
const { segmentationService, viewportGridService } = servicesManager.services;
segmentationService.removeSegmentationRepresentations(
viewportGridService.getActiveViewportId(),
{ segmentationId }
);
removeSegmentationFromViewportCommand: ({ segmentationId: displaySetInstanceUID }) => {
const { viewportGridService } = servicesManager.services;
const viewportId = viewportGridService.getActiveViewportId();

commandsManager.runCommand('removeDisplaySetLayer', {
viewportId,
displaySetInstanceUID,
});
},

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2567,12 +2567,12 @@ describe('SegmentationService', () => {
describe('clearSegmentationRepresentations', () => {
it('should clear the segmentation representations', () => {
const viewportId = 'viewportId';
jest.spyOn(service, 'removeSegmentationRepresentations').mockReturnValue(undefined);
jest.spyOn(service, 'removeRepresentationsFromViewport').mockReturnValue(undefined);

service.clearSegmentationRepresentations(viewportId);

expect(service.removeSegmentationRepresentations).toHaveBeenCalledTimes(1);
expect(service.removeSegmentationRepresentations).toHaveBeenCalledWith(viewportId);
expect(service.removeRepresentationsFromViewport).toHaveBeenCalledTimes(1);
expect(service.removeRepresentationsFromViewport).toHaveBeenCalledWith(viewportId);
});
});

Expand Down Expand Up @@ -2600,7 +2600,7 @@ describe('SegmentationService', () => {
});
});

describe('removeSegmentationRepresentations', () => {
describe('removeRepresentationsFromViewport', () => {
it('should remove the segmentation representations', () => {
const viewportId = 'viewportId';
const specifier = {
Expand All @@ -2609,7 +2609,7 @@ describe('SegmentationService', () => {
};
jest.spyOn(cstSegmentation, 'removeSegmentationRepresentations').mockReturnValue(undefined);

service.removeSegmentationRepresentations(viewportId, specifier);
service.removeRepresentationsFromViewport(viewportId, specifier);

expect(cstSegmentation.removeSegmentationRepresentations).toHaveBeenCalledTimes(1);
expect(cstSegmentation.removeSegmentationRepresentations).toHaveBeenCalledWith(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -247,7 +247,7 @@ class SegmentationService extends PubSubService {

eventTarget.removeEventListener(
csToolsEnums.Events.SEGMENTATION_REMOVED,
this._onSegmentationModifiedFromSource
this._onSegmentationRemovedFromSource
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this makes more sense than what was there before. Now we actually fire the SEGMENTATION_REMOVED event!

);

eventTarget.removeEventListener(
Expand All @@ -267,7 +267,7 @@ class SegmentationService extends PubSubService {

eventTarget.removeEventListener(
csToolsEnums.Events.SEGMENTATION_REPRESENTATION_REMOVED,
this._onSegmentationRepresentationModifiedFromSource
this._onSegmentationRepresentationRemovedFromSource
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same goes for this one.

);

eventTarget.removeEventListener(
Expand Down Expand Up @@ -1284,14 +1284,14 @@ class SegmentationService extends PubSubService {

/**
* Clears segmentation representations from the viewport.
* Unlike removeSegmentationRepresentations, this doesn't update
* Unlike removeRepresentationsFromViewport, this doesn't update
* removed display set and representation maps.
* We track removed segmentations manually to avoid re-adding them
* when the display set is added again.
* @param viewportId - The viewport ID to clear segmentation representations from.
*/
public clearSegmentationRepresentations(viewportId: string): void {
this.removeSegmentationRepresentations(viewportId);
this.removeRepresentationsFromViewport(viewportId);
}

/**
Expand All @@ -1307,7 +1307,7 @@ class SegmentationService extends PubSubService {
}

/**
* It removes the segmentation representations from the viewport.
* Removes segmentation representations from the viewport.
* @param viewportId - The viewport id to remove the segmentation representations from.
* @param specifier - The specifier to remove the segmentation representations.
*
Expand All @@ -1317,7 +1317,7 @@ class SegmentationService extends PubSubService {
* If a type specifier is provided, only the segmentation representation with the specified type are removed.
* If both a segmentationId and type specifier are provided, only the segmentation representation with the specified segmentationId and type are removed.
*/
public removeSegmentationRepresentations(
public removeRepresentationsFromViewport(
viewportId: string,
specifier: {
segmentationId?: string;
Expand Down Expand Up @@ -1861,7 +1861,7 @@ class SegmentationService extends PubSubService {

eventTarget.addEventListener(
csToolsEnums.Events.SEGMENTATION_REMOVED,
this._onSegmentationModifiedFromSource
this._onSegmentationRemovedFromSource
);

eventTarget.addEventListener(
Expand All @@ -1881,7 +1881,7 @@ class SegmentationService extends PubSubService {

eventTarget.addEventListener(
csToolsEnums.Events.SEGMENTATION_REPRESENTATION_REMOVED,
this._onSegmentationRepresentationModifiedFromSource
this._onSegmentationRepresentationRemovedFromSource
);

eventTarget.addEventListener(
Expand Down Expand Up @@ -2120,6 +2120,14 @@ class SegmentationService extends PubSubService {
});
};

private _onSegmentationRepresentationRemovedFromSource = evt => {
const { segmentationId, viewportId } = evt.detail;
this._broadcastEvent(this.EVENTS.SEGMENTATION_REPRESENTATION_REMOVED, {
segmentationId,
viewportId,
});
};

private _onSegmentationModifiedFromSource = (
evt: cstTypes.EventTypes.SegmentationModifiedEventType
) => {
Expand All @@ -2140,6 +2148,16 @@ class SegmentationService extends PubSubService {
});
};

private _onSegmentationRemovedFromSource = (
evt: cstTypes.EventTypes.SegmentationRemovedEventType
) => {
const { segmentationId } = evt.detail;

this._broadcastEvent(this.EVENTS.SEGMENTATION_REMOVED, {
segmentationId,
});
};

private _onAnnotationCutMergeProcessCompletedFromSource = evt => {
const { segmentationId } = evt.detail;
this._broadcastEvent(this.EVENTS.SEGMENTATION_ANNOTATION_CUT_MERGE_PROCESS_COMPLETED, {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ describe('setUpSegmentationEventHandlers', () => {
const mockSegmentationService = {
EVENTS: {
SEGMENTATION_ADDED: 'SEGMENTATION_ADDED',
SEGMENTATION_REMOVED: 'SEGMENTATION_REMOVED',
},
subscribe: jest.fn(),
getSegmentation: jest.fn(),
Expand All @@ -40,6 +41,7 @@ describe('setUpSegmentationEventHandlers', () => {
const mockUnsubscribeDataModified = jest.fn();
const mockUnsubscribeModified = jest.fn();
const mockUnsubscribeCreated = jest.fn();
const mockUnsubscribeRemoved = jest.fn();
const mockUnsubscribeSelectedSegmentationsForViewportEvents = [jest.fn(), jest.fn()];

const defaultParameters = {
Expand All @@ -59,8 +61,14 @@ describe('setUpSegmentationEventHandlers', () => {
unsubscribeSelectedSegmentationsForViewportEvents:
mockUnsubscribeSelectedSegmentationsForViewportEvents,
});
mockSegmentationService.subscribe.mockReturnValue({
unsubscribe: mockUnsubscribeCreated,
mockSegmentationService.subscribe.mockImplementation((eventName: string) => {
if (eventName === mockSegmentationService.EVENTS.SEGMENTATION_ADDED) {
return { unsubscribe: mockUnsubscribeCreated };
}
if (eventName === mockSegmentationService.EVENTS.SEGMENTATION_REMOVED) {
return { unsubscribe: mockUnsubscribeRemoved };
}
return { unsubscribe: jest.fn() };
});
});

Expand Down Expand Up @@ -99,6 +107,7 @@ describe('setUpSegmentationEventHandlers', () => {
mockUnsubscribeDataModified,
mockUnsubscribeModified,
mockUnsubscribeCreated,
mockUnsubscribeRemoved,
...mockUnsubscribeSelectedSegmentationsForViewportEvents,
],
});
Expand Down Expand Up @@ -358,5 +367,6 @@ describe('setUpSegmentationEventHandlers', () => {
expect(mockUnsubscribeDataModified).toHaveBeenCalled();
expect(mockUnsubscribeModified).toHaveBeenCalled();
expect(mockUnsubscribeCreated).toHaveBeenCalled();
expect(mockUnsubscribeRemoved).toHaveBeenCalled();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ import {
} from './segmentationHandlers';

export const setUpSegmentationEventHandlers = ({ servicesManager, commandsManager }) => {
const { segmentationService, customizationService, displaySetService } = servicesManager.services;
const { segmentationService, customizationService, displaySetService, viewportGridService } =
servicesManager.services;

const { unsubscribe: unsubscribeSegmentationDataModifiedHandler } =
setupSegmentationDataModifiedHandler({
Expand Down Expand Up @@ -57,6 +58,36 @@ export const setUpSegmentationEventHandlers = ({ servicesManager, commandsManage
}
);

const { unsubscribe: unsubscribeSegmentationRemoved } = segmentationService.subscribe(
segmentationService.EVENTS.SEGMENTATION_REMOVED,
({ segmentationId }) => {
const displaySet = displaySetService.getDisplaySetByUID(segmentationId);

// Remove the display set layer from all viewports that have it
if (displaySet) {
const state = viewportGridService.getState();
const viewports = state.viewports;

// Find all viewports that contain this segmentation's display set as a layer
for (const [viewportId, viewport] of viewports.entries()) {
const displaySetInstanceUIDs = viewport.displaySetInstanceUIDs || [];
if (displaySetInstanceUIDs.includes(segmentationId)) {
// Remove the display set layer from this viewport
commandsManager.runCommand('removeDisplaySetLayer', {
viewportId,
displaySetInstanceUID: segmentationId,
});
}
}

// Delete the display set from the service if it was made in client
if (displaySet.madeInClient) {
displaySetService.deleteDisplaySet(segmentationId);
}
}
}
);

const { unsubscribeSelectedSegmentationsForViewportEvents } =
setUpSelectedSegmentationsForViewportHandler({
segmentationService,
Expand All @@ -66,6 +97,7 @@ export const setUpSegmentationEventHandlers = ({ servicesManager, commandsManage
unsubscribeSegmentationDataModifiedHandler,
unsubscribeSegmentationModifiedHandler,
unsubscribeSegmentationCreated,
unsubscribeSegmentationRemoved,
...unsubscribeSelectedSegmentationsForViewportEvents,
];

Expand Down
25 changes: 15 additions & 10 deletions extensions/default/src/commandsModule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,8 @@ const commandsModule = ({
*/
addDisplaySetAsLayer: ({ viewportId, displaySetInstanceUID, removeFirst = false }) => {
if (!viewportId) {
const { activeViewportId } = servicesManager.services.viewportGridService.getState();
viewportId = activeViewportId;
const { activeViewportId } = servicesManager.services.viewportGridService.getState();
viewportId = activeViewportId;
}

if (!viewportId || !displaySetInstanceUID) {
Expand Down Expand Up @@ -151,6 +151,19 @@ const commandsModule = ({
return;
}

// Check if it's a segmentation and handle accordingly.
// Note that for the sake of hydrated segmentations, we remove the
// segmentation before checking if the display set is indeed in the viewport.
// This is because hydrated segmentations are not in the viewport per se
// {i.e. they are not layered) but are simply referenced by the display
// set in the viewport.
const isSegmentation = DERIVED_OVERLAY_MODALITIES.includes(displaySet.Modality);
if (isSegmentation) {
segmentationService.removeRepresentationsFromViewport(viewportId, {
segmentationId: displaySetInstanceUID,
});
}

// Get current display sets for the viewport
const currentDisplaySetUIDs = viewportGridService.getDisplaySetsUIDsForViewport(viewportId);

Expand All @@ -159,14 +172,6 @@ const commandsModule = ({
return;
}

// Check if it's a segmentation and handle accordingly
const isSegmentation = DERIVED_OVERLAY_MODALITIES.includes(displaySet.Modality);
if (isSegmentation) {
segmentationService.removeSegmentationRepresentations(viewportId, {
segmentationId: displaySetInstanceUID,
});
}

const updatedViewports = hangingProtocolService.getViewportsRequireUpdate(
viewportId,
displaySetInstanceUID
Expand Down
4 changes: 2 additions & 2 deletions platform/docs/docs/migration-guide/3p11-to-3p12/index.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
---
sidebar_position: 1
sidebar_label: 3.11 -> 3.12 beta
sidebar_label: 3.11 -> 3.12
---

# Migration Guide

This guide provides information about migrating from OHIF version 3.11 to version 3.12 beta
This guide provides information about migrating from OHIF version 3.11 to version 3.12

## Optional: Migrate modes to extend `modes/basic`

Expand Down
15 changes: 15 additions & 0 deletions platform/docs/docs/migration-guide/3p12-to-3p13/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
---
id: index
sidebar_position: 1
sidebar_label: 3.12 -> 3.13
title: 3.12 to 3.13 Migration Guide
---

import DocCardList from '@theme/DocCardList';
import { useCurrentSidebarCategory } from '@docusaurus/theme-common';

# 3.12 to 3.13 Migration Guide

This guide covers changes when upgrading from OHIF version 3.12 to version 3.13.

<DocCardList items={useCurrentSidebarCategory().items.filter(item => item.docId !== 'migration-guide/3p12-to-3p13/index')} />
Loading