Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(toolbox): Preserve user-specified tool state and streamline command execution #4063

Merged
merged 12 commits into from
Apr 29, 2024
Merged
30 changes: 9 additions & 21 deletions extensions/cornerstone-dicom-seg/src/getToolbarModule.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
export function getToolbarModule({ commandsManager, servicesManager }) {
const { segmentationService, toolGroupService } = servicesManager.services;
export function getToolbarModule({ servicesManager }) {
const { segmentationService, toolbarService, toolGroupService } = servicesManager.services;
return [
{
name: 'evaluate.cornerstone.segmentation',
Expand All @@ -20,12 +20,16 @@ export function getToolbarModule({ commandsManager, servicesManager }) {
const toolGroup = toolGroupService.getToolGroupForViewport(viewportId);

if (!toolGroup) {
return;
return {
disabled: true,
className: '!text-common-bright ohif-disabled',
disabledText: disabledText ?? 'Not available on the current viewport',
};
}

const toolName = getToolNameForButton(button);
const toolName = toolbarService.getToolNameForButton(button);

if (!toolGroup || !toolGroup.hasTool(toolName)) {
if (!toolGroup.hasTool(toolName) && !toolNames) {
return {
disabled: true,
className: '!text-common-bright ohif-disabled',
Expand All @@ -51,19 +55,3 @@ export function getToolbarModule({ commandsManager, servicesManager }) {
},
];
}

// Todo: this is duplicate, we should move it to a shared location
function getToolNameForButton(button) {
const { props } = button;

const commands = props?.commands || button.commands;
const commandsArray = Array.isArray(commands) ? commands : [commands];
const firstCommand = commandsArray[0];

if (firstCommand?.commandOptions) {
return firstCommand.commandOptions.toolName ?? props?.id ?? button.id;
}

// use id as a fallback for toolName
return props?.id ?? button.id;
}
Original file line number Diff line number Diff line change
Expand Up @@ -485,7 +485,10 @@ function _subscribeToJumpToMeasurementEvents(
cornerstoneViewportService.getViewportIdToJump(
jumpId,
measurement.displaySetInstanceUID,
{ referencedImageId: measurement.referencedImageId }
{
referencedImageId:
measurement.referencedImageId || measurement.metadata?.referencedImageId,
}
);
}
if (cacheJumpToMeasurementEvent.cornerstoneViewport !== viewportId) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ function CornerstoneImageScrollbar({

if (isCineEnabled) {
// on image scrollbar change, stop the CINE if it is playing
cineService.stopClip(element);
cineService.stopClip(element, { viewportId });
cineService.setCine({ id: viewportId, isPlaying: false });
}

Expand Down
12 changes: 10 additions & 2 deletions extensions/cornerstone/src/components/CinePlayer/CinePlayer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ function WrappedCinePlayer({ enabledVPElement, viewportId, servicesManager }) {
}
cineService.setCine({ id: viewportId, isPlaying, frameRate });
setNewStackFrameRate(frameRate);
}, [displaySetService, viewportId, viewportGridService, cines, isCineEnabled]);
}, [displaySetService, viewportId, viewportGridService, cines, isCineEnabled, enabledVPElement]);

useEffect(() => {
isMountedRef.current = true;
Expand All @@ -78,6 +78,14 @@ function WrappedCinePlayer({ enabledVPElement, viewportId, servicesManager }) {
};
}, [isCineEnabled, newDisplaySetHandler]);

useEffect(() => {
if (!isCineEnabled) {
return;
}

cineHandler();
}, [isCineEnabled, cineHandler, enabledVPElement]);

/**
* Use effect for handling new display set
*/
Expand Down Expand Up @@ -112,7 +120,7 @@ function WrappedCinePlayer({ enabledVPElement, viewportId, servicesManager }) {
cineHandler();

return () => {
cineService.stopClip(enabledVPElement);
cineService.stopClip(enabledVPElement, { viewportId });
};
}, [cines, viewportId, cineService, enabledVPElement, cineHandler]);

Expand Down
42 changes: 19 additions & 23 deletions extensions/cornerstone/src/getToolbarModule.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ const getToggledClassName = (isToggled: boolean) => {
export default function getToolbarModule({ commandsManager, servicesManager }) {
const {
toolGroupService,
toolbarService,
syncGroupService,
cornerstoneViewportService,
hangingProtocolService,
Expand All @@ -21,24 +22,26 @@ export default function getToolbarModule({ commandsManager, servicesManager }) {
// enabled or not
{
name: 'evaluate.cornerstoneTool',
evaluate: ({ viewportId, button, disabledText }) => {
evaluate: ({ viewportId, button, toolNames, disabledText }) => {
const toolGroup = toolGroupService.getToolGroupForViewport(viewportId);

if (!toolGroup) {
return;
}

const toolName = getToolNameForButton(button);
const toolName = toolbarService.getToolNameForButton(button);

if (!toolGroup || !toolGroup.hasTool(toolName)) {
if (!toolGroup || (!toolGroup.hasTool(toolName) && !toolNames)) {
return {
disabled: true,
className: '!text-common-bright ohif-disabled',
disabledText: disabledText ?? 'Not available on the current viewport',
};
}

const isPrimaryActive = toolGroup.getActivePrimaryMouseButtonTool() === toolName;
const isPrimaryActive = toolNames
? toolNames.includes(toolGroup.getActivePrimaryMouseButtonTool())
: toolGroup.getActivePrimaryMouseButtonTool() === toolName;

return {
disabled: false,
Expand Down Expand Up @@ -71,7 +74,7 @@ export default function getToolbarModule({ commandsManager, servicesManager }) {
// check if the active toolName is part of the items then we need
// to move it to the primary button
const activeToolIndex = items.findIndex(item => {
const toolName = getToolNameForButton(item);
const toolName = toolbarService.getToolNameForButton(item);
return toolName === activeToolName;
});

Expand Down Expand Up @@ -114,6 +117,7 @@ export default function getToolbarModule({ commandsManager, servicesManager }) {
_evaluateToggle({
viewportId,
button,
toolbarService,
disabledText,
offModes: [Enums.ToolModes.Disabled],
toolGroupService,
Expand All @@ -125,6 +129,7 @@ export default function getToolbarModule({ commandsManager, servicesManager }) {
_evaluateToggle({
viewportId,
button,
toolbarService,
disabledText,
offModes: [Enums.ToolModes.Disabled, Enums.ToolModes.Passive],
toolGroupService,
Expand Down Expand Up @@ -267,13 +272,20 @@ export default function getToolbarModule({ commandsManager, servicesManager }) {
];
}

function _evaluateToggle({ viewportId, button, disabledText, offModes, toolGroupService }) {
function _evaluateToggle({
viewportId,
toolbarService,
button,
disabledText,
offModes,
toolGroupService,
}) {
const toolGroup = toolGroupService.getToolGroupForViewport(viewportId);

if (!toolGroup) {
return;
}
const toolName = getToolNameForButton(button);
const toolName = toolbarService.getToolNameForButton(button);

if (!toolGroup.hasTool(toolName)) {
return {
Expand All @@ -289,19 +301,3 @@ function _evaluateToggle({ viewportId, button, disabledText, offModes, toolGroup
className: getToggledClassName(!isOff),
};
}

// Todo: this is duplicate, we should move it to a shared location
function getToolNameForButton(button) {
const { props } = button;

const commands = props?.commands || button.commands;
const commandsArray = Array.isArray(commands) ? commands : [commands];
const firstCommand = commandsArray[0];

if (firstCommand?.commandOptions) {
return firstCommand.commandOptions.toolName ?? props?.id ?? button.id;
}

// use id as a fallback for toolName
return props?.id ?? button.id;
}
Original file line number Diff line number Diff line change
Expand Up @@ -532,6 +532,11 @@ class CornerstoneViewportService extends PubSubService implements IViewportServi
cameraProps: unknown
): string {
const viewportInfo = this.getViewportInfo(activeViewportId);

if (viewportInfo.getViewportType() === csEnums.ViewportType.VOLUME_3D) {
return null;
}

const { referencedImageId } = cameraProps;
if (viewportInfo?.contains(displaySetInstanceUID, referencedImageId)) {
return activeViewportId;
Expand Down
47 changes: 40 additions & 7 deletions extensions/cornerstone/src/services/ViewportService/Viewport.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
import { Types, Enums } from '@cornerstonejs/core';
import {
Types,
Enums,
getEnabledElementByViewportId,
VolumeViewport,
utilities,
} from '@cornerstonejs/core';
import { Types as CoreTypes } from '@ohif/core';
import { StackViewportData, VolumeViewportData } from '../../types/CornerstoneCacheService';
import getCornerstoneBlendMode from '../../utils/getCornerstoneBlendMode';
Expand Down Expand Up @@ -89,13 +95,30 @@ const DEFAULT_TOOLGROUP_ID = 'default';

// Return true if the data contains the given display set UID OR the imageId
// if it is a composite object.
const dataContains = (data, displaySetUID: string, imageId?: string): boolean => {
if (data.displaySetInstanceUID === displaySetUID) {
return true;
}
const dataContains = ({ data, displaySetUID, imageId, viewport }): boolean => {
if (imageId && data.isCompositeStack && data.imageIds) {
return !!data.imageIds.find(dataId => dataId === imageId);
}

if (imageId && (data.volumeId || viewport instanceof VolumeViewport)) {
const isAcquisition = !!viewport.getCurrentImageId();

if (!isAcquisition) {
return false;
}

const imageURI = utilities.imageIdToURI(imageId);
const hasImageId = viewport.hasImageURI(imageURI);

if (hasImageId) {
return true;
}
}

if (data.displaySetInstanceUID === displaySetUID) {
return true;
}

return false;
};

Expand All @@ -122,10 +145,20 @@ class ViewportInfo {
return false;
}

const { viewport } = getEnabledElementByViewportId(this.viewportId) || {};

if (this.viewportData.data.length) {
return !!this.viewportData.data.find(data => dataContains(data, displaySetUID, imageId));
return !!this.viewportData.data.find(data =>
dataContains({ data, displaySetUID, imageId, viewport })
);
}
return dataContains(this.viewportData.data, displaySetUID, imageId);

return dataContains({
data: this.viewportData.data,
displaySetUID,
imageId,
viewport,
});
}

public destroy = (): void => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ function getMappedAnnotations(annotation, DisplaySetService) {
unit: modalityUnit,
mean,
stdDev,
metadata,
max,
area,
areaUnit,
Expand Down
15 changes: 0 additions & 15 deletions modes/preclinical-4d/src/segmentationButtons.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,5 @@
import type { Button } from '@ohif/core/types';

function _createSetToolActiveCommands(toolName) {
return [
{
commandName: 'setToolActive',
commandOptions: {
toolName,
},
},
];
}

const toolbarButtons: Button[] = [
{
id: 'BrushTools',
Expand All @@ -26,7 +15,6 @@ const toolbarButtons: Button[] = [
name: 'evaluate.cornerstone.segmentation',
toolNames: ['CircularBrush', 'SphereBrush'],
},
commands: _createSetToolActiveCommands('CircularBrush'),
options: [
{
name: 'Size (mm)',
Expand Down Expand Up @@ -62,7 +50,6 @@ const toolbarButtons: Button[] = [
name: 'evaluate.cornerstone.segmentation',
toolNames: ['CircularEraser', 'SphereEraser'],
},
commands: _createSetToolActiveCommands('CircularEraser'),
options: [
{
name: 'Radius (mm)',
Expand Down Expand Up @@ -98,7 +85,6 @@ const toolbarButtons: Button[] = [
name: 'evaluate.cornerstone.segmentation',
toolNames: ['ThresholdCircularBrush', 'ThresholdSphereBrush'],
},
commands: _createSetToolActiveCommands('ThresholdCircularBrush'),
options: [
{
name: 'Radius (mm)',
Expand Down Expand Up @@ -156,7 +142,6 @@ const toolbarButtons: Button[] = [
toolNames: ['CircleScissor', 'SphereScissor', 'RectangleScissor'],
},
icon: 'icon-tool-shape',
commands: _createSetToolActiveCommands('CircleScissor'),
options: [
{
name: 'Shape',
Expand Down
Loading
Loading