fix(toolbox): Preserve user-specified tool state and streamline command execution (#4063)
This commit is contained in:
parent
4784638717
commit
f1a736d193
@ -47,7 +47,7 @@ provided by the <a href="https://ohif.org/">Open Health Imaging Foundation (OHIF
|
||||
| <img src="https://github.com/OHIF/Viewers/blob/master/platform/docs/docs/assets/img/demo-volume-rendering.webp?raw=true" alt="Volume Rendering" width="350"/> | Volume Rendering | [Demo](https://viewer.ohif.org/viewer?StudyInstanceUIDs=1.3.6.1.4.1.25403.345050719074.3824.20170125095438.5&hangingprotocolId=mprAnd3DVolumeViewport) |
|
||||
| <img src="https://github.com/OHIF/Viewers/blob/master/platform/docs/docs/assets/img/demo-pdf.webp?raw=true" alt="PDF" width="350"/> | PDF | [Demo](https://viewer.ohif.org/viewer?StudyInstanceUIDs=2.25.317377619501274872606137091638706705333) |
|
||||
| <img src="https://github.com/OHIF/Viewers/blob/master/platform/docs/docs/assets/img/demo-rtstruct.webp?raw=true" alt="RTSTRUCT" width="350"/> | RT STRUCT | [Demo](https://viewer.ohif.org/viewer?StudyInstanceUIDs=1.3.6.1.4.1.5962.99.1.2968617883.1314880426.1493322302363.3.0) |
|
||||
| <img src="https://github.com/OHIF/Viewers/blob/master/platform/docs/docs/assets/img/demo-4D.webp?raw=true" alt="4D" width="350"/> | 4D | [Demo](https://viewer.ohif.org/dynamic-volume?StudyInstanceUIDs=2.25.232704420736447710317909004159492840763) |
|
||||
| <img src="https://github.com/OHIF/Viewers/blob/master/platform/docs/docs/assets/img/demo-4d.webp?raw=true" alt="4D" width="350"/> | 4D | [Demo](https://viewer.ohif.org/dynamic-volume?StudyInstanceUIDs=2.25.232704420736447710317909004159492840763) |
|
||||
| <img src="https://github.com/OHIF/Viewers/blob/master/platform/docs/docs/assets/img/demo-video.webp?raw=true" alt="VIDEO" width="350"/> | Video | [Demo](https://viewer.ohif.org/viewer?StudyInstanceUIDs=2.25.96975534054447904995905761963464388233) |
|
||||
| <img src="https://github.com/OHIF/Viewers/blob/master/platform/docs/docs/assets/img/microscopy.webp?raw=true" alt="microscopy" width="350"/> | Slide Microscopy | [Demo](https://viewer.ohif.org/microscopy?StudyInstanceUIDs=2.25.141277760791347900862109212450152067508) |
|
||||
|
||||
|
||||
@ -46,9 +46,9 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.20.13",
|
||||
"@cornerstonejs/adapters": "^1.70.13",
|
||||
"@cornerstonejs/core": "^1.70.13",
|
||||
"@kitware/vtk.js": "30.3.3",
|
||||
"@cornerstonejs/adapters": "^1.70.14",
|
||||
"@cornerstonejs/core": "^1.70.14",
|
||||
"@kitware/vtk.js": "30.4.1",
|
||||
"react-color": "^2.19.3"
|
||||
}
|
||||
}
|
||||
|
||||
@ -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',
|
||||
@ -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',
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -46,9 +46,9 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.20.13",
|
||||
"@cornerstonejs/adapters": "^1.70.13",
|
||||
"@cornerstonejs/core": "^1.70.13",
|
||||
"@cornerstonejs/tools": "^1.70.13",
|
||||
"@cornerstonejs/adapters": "^1.70.14",
|
||||
"@cornerstonejs/core": "^1.70.14",
|
||||
"@cornerstonejs/tools": "^1.70.14",
|
||||
"classnames": "^2.3.2"
|
||||
}
|
||||
}
|
||||
|
||||
@ -42,9 +42,9 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.20.13",
|
||||
"@cornerstonejs/core": "^1.70.13",
|
||||
"@cornerstonejs/streaming-image-volume-loader": "^1.70.13",
|
||||
"@cornerstonejs/tools": "^1.70.13",
|
||||
"@cornerstonejs/core": "^1.70.14",
|
||||
"@cornerstonejs/streaming-image-volume-loader": "^1.70.14",
|
||||
"@cornerstonejs/tools": "^1.70.14",
|
||||
"classnames": "^2.3.2"
|
||||
}
|
||||
}
|
||||
|
||||
@ -38,7 +38,7 @@
|
||||
"@cornerstonejs/codec-libjpeg-turbo-8bit": "^1.2.2",
|
||||
"@cornerstonejs/codec-openjpeg": "^1.2.2",
|
||||
"@cornerstonejs/codec-openjph": "^2.4.2",
|
||||
"@cornerstonejs/dicom-image-loader": "^1.70.13",
|
||||
"@cornerstonejs/dicom-image-loader": "^1.70.14",
|
||||
"@icr/polyseg-wasm": "^0.4.0",
|
||||
"@ohif/core": "3.8.0-beta.92",
|
||||
"@ohif/ui": "3.8.0-beta.92",
|
||||
@ -55,12 +55,12 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.20.13",
|
||||
"@cornerstonejs/adapters": "^1.70.13",
|
||||
"@cornerstonejs/core": "^1.70.13",
|
||||
"@cornerstonejs/streaming-image-volume-loader": "^1.70.13",
|
||||
"@cornerstonejs/tools": "^1.70.13",
|
||||
"@cornerstonejs/adapters": "^1.70.14",
|
||||
"@cornerstonejs/core": "^1.70.14",
|
||||
"@cornerstonejs/streaming-image-volume-loader": "^1.70.14",
|
||||
"@cornerstonejs/tools": "^1.70.14",
|
||||
"@icr/polyseg-wasm": "^0.4.0",
|
||||
"@kitware/vtk.js": "30.3.3",
|
||||
"@kitware/vtk.js": "30.4.1",
|
||||
"html2canvas": "^1.4.1",
|
||||
"lodash.debounce": "4.0.8",
|
||||
"lodash.merge": "^4.6.2",
|
||||
|
||||
@ -485,7 +485,10 @@ function _subscribeToJumpToMeasurementEvents(
|
||||
cornerstoneViewportService.getViewportIdToJump(
|
||||
jumpId,
|
||||
measurement.displaySetInstanceUID,
|
||||
{ referencedImageId: measurement.referencedImageId }
|
||||
{
|
||||
referencedImageId:
|
||||
measurement.referencedImageId || measurement.metadata?.referencedImageId,
|
||||
}
|
||||
);
|
||||
}
|
||||
if (cacheJumpToMeasurementEvent.cornerstoneViewport !== viewportId) {
|
||||
@ -594,6 +597,9 @@ function _jumpToMeasurement(
|
||||
i => i.SOPInstanceUID === SOPInstanceUID
|
||||
);
|
||||
|
||||
// the index is reversed in the volume viewport
|
||||
// imageIdIndex = referencedDisplaySet.images.length - 1 - imageIdIndex;
|
||||
|
||||
const { viewPlaneNormal: viewportViewPlane } = viewport.getCamera();
|
||||
|
||||
// should compare abs for both planes since the direction can be flipped
|
||||
|
||||
@ -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 });
|
||||
}
|
||||
|
||||
|
||||
@ -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;
|
||||
@ -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
|
||||
*/
|
||||
@ -112,7 +120,7 @@ function WrappedCinePlayer({ enabledVPElement, viewportId, servicesManager }) {
|
||||
cineHandler();
|
||||
|
||||
return () => {
|
||||
cineService.stopClip(enabledVPElement);
|
||||
cineService.stopClip(enabledVPElement, { viewportId });
|
||||
};
|
||||
}, [cines, viewportId, cineService, enabledVPElement, cineHandler]);
|
||||
|
||||
|
||||
@ -9,6 +9,7 @@ const getToggledClassName = (isToggled: boolean) => {
|
||||
export default function getToolbarModule({ commandsManager, servicesManager }) {
|
||||
const {
|
||||
toolGroupService,
|
||||
toolbarService,
|
||||
syncGroupService,
|
||||
cornerstoneViewportService,
|
||||
hangingProtocolService,
|
||||
@ -21,16 +22,16 @@ 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',
|
||||
@ -38,7 +39,9 @@ export default function getToolbarModule({ commandsManager, servicesManager }) {
|
||||
};
|
||||
}
|
||||
|
||||
const isPrimaryActive = toolGroup.getActivePrimaryMouseButtonTool() === toolName;
|
||||
const isPrimaryActive = toolNames
|
||||
? toolNames.includes(toolGroup.getActivePrimaryMouseButtonTool())
|
||||
: toolGroup.getActivePrimaryMouseButtonTool() === toolName;
|
||||
|
||||
return {
|
||||
disabled: false,
|
||||
@ -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;
|
||||
});
|
||||
|
||||
@ -114,6 +117,7 @@ export default function getToolbarModule({ commandsManager, servicesManager }) {
|
||||
_evaluateToggle({
|
||||
viewportId,
|
||||
button,
|
||||
toolbarService,
|
||||
disabledText,
|
||||
offModes: [Enums.ToolModes.Disabled],
|
||||
toolGroupService,
|
||||
@ -125,6 +129,7 @@ export default function getToolbarModule({ commandsManager, servicesManager }) {
|
||||
_evaluateToggle({
|
||||
viewportId,
|
||||
button,
|
||||
toolbarService,
|
||||
disabledText,
|
||||
offModes: [Enums.ToolModes.Disabled, Enums.ToolModes.Passive],
|
||||
toolGroupService,
|
||||
@ -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 {
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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';
|
||||
@ -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;
|
||||
};
|
||||
|
||||
@ -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 => {
|
||||
|
||||
@ -114,6 +114,7 @@ function getMappedAnnotations(annotation, DisplaySetService) {
|
||||
unit: modalityUnit,
|
||||
mean,
|
||||
stdDev,
|
||||
metadata,
|
||||
max,
|
||||
area,
|
||||
areaUnit,
|
||||
|
||||
@ -32,8 +32,8 @@
|
||||
"start": "yarn run dev"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@cornerstonejs/core": "^1.70.13",
|
||||
"@cornerstonejs/tools": "^1.70.13",
|
||||
"@cornerstonejs/core": "^1.70.14",
|
||||
"@cornerstonejs/tools": "^1.70.14",
|
||||
"@ohif/core": "3.8.0-beta.92",
|
||||
"@ohif/extension-cornerstone-dicom-sr": "3.8.0-beta.92",
|
||||
"@ohif/ui": "3.8.0-beta.92",
|
||||
|
||||
@ -1,16 +1,5 @@
|
||||
import type { Button } from '@ohif/core/types';
|
||||
|
||||
function _createSetToolActiveCommands(toolName) {
|
||||
return [
|
||||
{
|
||||
commandName: 'setToolActive',
|
||||
commandOptions: {
|
||||
toolName,
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
const toolbarButtons: Button[] = [
|
||||
{
|
||||
id: 'BrushTools',
|
||||
@ -26,7 +15,6 @@ const toolbarButtons: Button[] = [
|
||||
name: 'evaluate.cornerstone.segmentation',
|
||||
toolNames: ['CircularBrush', 'SphereBrush'],
|
||||
},
|
||||
commands: _createSetToolActiveCommands('CircularBrush'),
|
||||
options: [
|
||||
{
|
||||
name: 'Size (mm)',
|
||||
@ -62,7 +50,6 @@ const toolbarButtons: Button[] = [
|
||||
name: 'evaluate.cornerstone.segmentation',
|
||||
toolNames: ['CircularEraser', 'SphereEraser'],
|
||||
},
|
||||
commands: _createSetToolActiveCommands('CircularEraser'),
|
||||
options: [
|
||||
{
|
||||
name: 'Radius (mm)',
|
||||
@ -98,7 +85,6 @@ const toolbarButtons: Button[] = [
|
||||
name: 'evaluate.cornerstone.segmentation',
|
||||
toolNames: ['ThresholdCircularBrush', 'ThresholdSphereBrush'],
|
||||
},
|
||||
commands: _createSetToolActiveCommands('ThresholdCircularBrush'),
|
||||
options: [
|
||||
{
|
||||
name: 'Radius (mm)',
|
||||
@ -156,7 +142,6 @@ const toolbarButtons: Button[] = [
|
||||
toolNames: ['CircleScissor', 'SphereScissor', 'RectangleScissor'],
|
||||
},
|
||||
icon: 'icon-tool-shape',
|
||||
commands: _createSetToolActiveCommands('CircleScissor'),
|
||||
options: [
|
||||
{
|
||||
name: 'Shape',
|
||||
|
||||
@ -1,16 +1,5 @@
|
||||
import type { Button } from '@ohif/core/types';
|
||||
|
||||
function _createSetToolActiveCommands(toolName) {
|
||||
return [
|
||||
{
|
||||
commandName: 'setToolActive',
|
||||
commandOptions: {
|
||||
toolName,
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
const toolbarButtons: Button[] = [
|
||||
{
|
||||
id: 'BrushTools',
|
||||
@ -27,7 +16,6 @@ const toolbarButtons: Button[] = [
|
||||
toolNames: ['CircularBrush', 'SphereBrush'],
|
||||
disabledText: 'Create new segmentation to enable this tool.',
|
||||
},
|
||||
commands: _createSetToolActiveCommands('CircularBrush'),
|
||||
options: [
|
||||
{
|
||||
name: 'Radius (mm)',
|
||||
@ -63,7 +51,6 @@ const toolbarButtons: Button[] = [
|
||||
name: 'evaluate.cornerstone.segmentation',
|
||||
toolNames: ['CircularEraser', 'SphereEraser'],
|
||||
},
|
||||
commands: _createSetToolActiveCommands('CircularEraser'),
|
||||
options: [
|
||||
{
|
||||
name: 'Radius (mm)',
|
||||
@ -99,7 +86,6 @@ const toolbarButtons: Button[] = [
|
||||
name: 'evaluate.cornerstone.segmentation',
|
||||
toolNames: ['ThresholdCircularBrush', 'ThresholdSphereBrush'],
|
||||
},
|
||||
commands: _createSetToolActiveCommands('ThresholdCircularBrush'),
|
||||
options: [
|
||||
{
|
||||
name: 'Radius (mm)',
|
||||
@ -130,22 +116,29 @@ const toolbarButtons: Button[] = [
|
||||
{ value: 'ThresholdDynamic', label: 'Dynamic' },
|
||||
{ value: 'ThresholdRange', label: 'Range' },
|
||||
],
|
||||
commands: ({ value, commandsManager }) => {
|
||||
commands: ({ value, commandsManager, options }) => {
|
||||
if (value === 'ThresholdDynamic') {
|
||||
commandsManager.run('setToolActive', {
|
||||
toolName: 'ThresholdCircularBrushDynamic',
|
||||
});
|
||||
} else {
|
||||
commandsManager.run('setToolActive', {
|
||||
toolName: 'ThresholdCircularBrush',
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// check the condition of the threshold-range option
|
||||
const thresholdRangeOption = options.find(
|
||||
option => option.id === 'threshold-shape'
|
||||
);
|
||||
|
||||
commandsManager.run('setToolActiveToolbar', {
|
||||
toolName: thresholdRangeOption.value,
|
||||
});
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'Shape',
|
||||
type: 'radio',
|
||||
id: 'eraser-mode',
|
||||
id: 'threshold-shape',
|
||||
value: 'ThresholdCircularBrush',
|
||||
values: [
|
||||
{ value: 'ThresholdCircularBrush', label: 'Circle' },
|
||||
@ -162,7 +155,7 @@ const toolbarButtons: Button[] = [
|
||||
min: -1000,
|
||||
max: 1000,
|
||||
step: 1,
|
||||
values: [100, 600],
|
||||
value: [100, 600],
|
||||
condition: ({ options }) =>
|
||||
options.find(option => option.id === 'dynamic-mode').value === 'ThresholdRange',
|
||||
commands: {
|
||||
@ -187,7 +180,6 @@ const toolbarButtons: Button[] = [
|
||||
toolNames: ['CircleScissor', 'SphereScissor', 'RectangleScissor'],
|
||||
},
|
||||
icon: 'icon-tool-shape',
|
||||
commands: _createSetToolActiveCommands('CircleScissor'),
|
||||
options: [
|
||||
{
|
||||
name: 'Shape',
|
||||
|
||||
@ -8,18 +8,6 @@ const setToolActiveToolbar = {
|
||||
},
|
||||
};
|
||||
|
||||
function _createSetToolActiveCommands(toolName) {
|
||||
return [
|
||||
{
|
||||
commandName: 'setToolActiveToolbar',
|
||||
commandOptions: {
|
||||
toolName,
|
||||
toolGroupIds: [toolGroupIds.CT, toolGroupIds.PT, toolGroupIds.Fusion],
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
const toolbarButtons = [
|
||||
{
|
||||
id: 'MeasurementTools',
|
||||
@ -144,7 +132,6 @@ const toolbarButtons = [
|
||||
toolNames: ['CircularBrush', 'SphereBrush'],
|
||||
disabledText: 'Create new segmentation to enable this tool.',
|
||||
},
|
||||
commands: _createSetToolActiveCommands('CircularBrush'),
|
||||
options: [
|
||||
{
|
||||
name: 'Radius (mm)',
|
||||
@ -180,7 +167,6 @@ const toolbarButtons = [
|
||||
name: 'evaluate.cornerstone.segmentation',
|
||||
toolNames: ['CircularEraser', 'SphereEraser'],
|
||||
},
|
||||
commands: _createSetToolActiveCommands('CircularEraser'),
|
||||
options: [
|
||||
{
|
||||
name: 'Radius (mm)',
|
||||
@ -216,7 +202,6 @@ const toolbarButtons = [
|
||||
name: 'evaluate.cornerstone.segmentation',
|
||||
toolNames: ['ThresholdCircularBrush', 'ThresholdSphereBrush'],
|
||||
},
|
||||
commands: _createSetToolActiveCommands('ThresholdCircularBrush'),
|
||||
options: [
|
||||
{
|
||||
name: 'Radius (mm)',
|
||||
|
||||
@ -60,7 +60,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.20.13",
|
||||
"@kitware/vtk.js": "30.3.3",
|
||||
"@kitware/vtk.js": "30.4.1",
|
||||
"core-js": "^3.2.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
|
||||
@ -54,7 +54,7 @@
|
||||
"@cornerstonejs/codec-libjpeg-turbo-8bit": "^1.2.2",
|
||||
"@cornerstonejs/codec-openjpeg": "^1.2.2",
|
||||
"@cornerstonejs/codec-openjph": "^2.4.5",
|
||||
"@cornerstonejs/dicom-image-loader": "^1.70.13",
|
||||
"@cornerstonejs/dicom-image-loader": "^1.70.14",
|
||||
"@emotion/serialize": "^1.1.3",
|
||||
"@ohif/core": "3.8.0-beta.92",
|
||||
"@ohif/extension-cornerstone": "3.8.0-beta.92",
|
||||
|
||||
@ -125,11 +125,12 @@ function Local({ modePath }: LocalProps) {
|
||||
>
|
||||
<div className="flex h-screen w-screen items-center justify-center ">
|
||||
<div className="bg-secondary-dark mx-auto space-y-2 rounded-lg py-8 px-8 drop-shadow-md">
|
||||
<img
|
||||
className="mx-auto block h-14"
|
||||
src="./ohif-logo.svg"
|
||||
alt="OHIF"
|
||||
/>
|
||||
<div className="flex items-center justify-center">
|
||||
<Icon
|
||||
name="logo-dark-background"
|
||||
className="h-28"
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-2 pt-4 text-center">
|
||||
{dropInitiated ? (
|
||||
<div className="flex flex-col items-center justify-center pt-48">
|
||||
|
||||
@ -37,7 +37,7 @@
|
||||
"@cornerstonejs/codec-libjpeg-turbo-8bit": "^1.2.2",
|
||||
"@cornerstonejs/codec-openjpeg": "^1.2.2",
|
||||
"@cornerstonejs/codec-openjph": "^2.4.2",
|
||||
"@cornerstonejs/dicom-image-loader": "^1.70.13",
|
||||
"@cornerstonejs/dicom-image-loader": "^1.70.14",
|
||||
"@ohif/ui": "3.8.0-beta.92",
|
||||
"cornerstone-math": "0.1.9",
|
||||
"dicom-parser": "^1.8.21"
|
||||
|
||||
@ -204,7 +204,8 @@ export class CommandsManager {
|
||||
|
||||
// Execute each command in the array
|
||||
let result: unknown;
|
||||
commands.forEach(({ commandName, commandOptions, context }) => {
|
||||
commands.forEach(command => {
|
||||
const { commandName, commandOptions, context } = command;
|
||||
if (commandName) {
|
||||
result = this.runCommand(
|
||||
commandName,
|
||||
@ -215,7 +216,11 @@ export class CommandsManager {
|
||||
context
|
||||
);
|
||||
} else {
|
||||
console.warn('No command name supplied in', toRun);
|
||||
if (typeof command === 'function') {
|
||||
result = command();
|
||||
} else {
|
||||
console.warn('No command name supplied in', toRun);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@ -156,7 +156,7 @@ export default class ToolbarService extends PubSubService {
|
||||
const itemId = interaction.itemId ?? interaction.id;
|
||||
interaction.itemId = itemId;
|
||||
|
||||
const commands = Array.isArray(interaction.commands)
|
||||
let commands = Array.isArray(interaction.commands)
|
||||
? interaction.commands
|
||||
: [interaction.commands];
|
||||
|
||||
@ -170,6 +170,27 @@ export default class ToolbarService extends PubSubService {
|
||||
|
||||
const commandOptions = { ...options, ...interaction };
|
||||
|
||||
commands = commands.map(command => {
|
||||
if (typeof command === 'function') {
|
||||
return () => {
|
||||
command({
|
||||
...commandOptions,
|
||||
commandsManager: this._commandsManager,
|
||||
servicesManager: this._servicesManager,
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
return command;
|
||||
});
|
||||
|
||||
// if still no commands, return
|
||||
commands = commands.filter(Boolean);
|
||||
|
||||
if (!commands.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Loop through commands and run them with the combined options
|
||||
this._commandsManager.run(commands, commandOptions);
|
||||
|
||||
@ -377,6 +398,26 @@ export default class ToolbarService extends PubSubService {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the tool name for a given button.
|
||||
* @param button - The button object.
|
||||
* @returns The tool name associated with the button.
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {*} btn
|
||||
|
||||
@ -44,7 +44,7 @@ Key features:
|
||||
| <img src="https://github.com/OHIF/Viewers/blob/master/platform/docs/docs/assets/img/demo-volume-rendering.webp?raw=true" alt="Volume Rendering" width="350"/> | Volume Rendering | [Demo](https://viewer.ohif.org/viewer?StudyInstanceUIDs=1.3.6.1.4.1.25403.345050719074.3824.20170125095438.5&hangingprotocolId=mprAnd3DVolumeViewport) |
|
||||
| <img src="https://github.com/OHIF/Viewers/blob/master/platform/docs/docs/assets/img/demo-pdf.webp?raw=true" alt="PDF" width="350"/> | PDF | [Demo](https://viewer.ohif.org/viewer?StudyInstanceUIDs=2.25.317377619501274872606137091638706705333) |
|
||||
| <img src="https://github.com/OHIF/Viewers/blob/master/platform/docs/docs/assets/img/demo-rtstruct.webp?raw=true" alt="RTSTRUCT" width="350"/> | RT STRUCT | [Demo](https://viewer.ohif.org/viewer?StudyInstanceUIDs=1.3.6.1.4.1.5962.99.1.2968617883.1314880426.1493322302363.3.0) |
|
||||
| <img src="https://github.com/OHIF/Viewers/blob/master/platform/docs/docs/assets/img/demo-4D.webp?raw=true" alt="4D" width="350"/> | 4D | [Demo](https://viewer.ohif.org/dynamic-volume?StudyInstanceUIDs=2.25.232704420736447710317909004159492840763) |
|
||||
| <img src="https://github.com/OHIF/Viewers/blob/master/platform/docs/docs/assets/img/demo-4d.webp?raw=true" alt="4D" width="350"/> | 4D | [Demo](https://viewer.ohif.org/dynamic-volume?StudyInstanceUIDs=2.25.232704420736447710317909004159492840763) |
|
||||
| <img src="https://github.com/OHIF/Viewers/blob/master/platform/docs/docs/assets/img/demo-video.webp?raw=true" alt="VIDEO" width="350"/> | Video | [Demo](https://viewer.ohif.org/viewer?StudyInstanceUIDs=2.25.96975534054447904995905761963464388233) |
|
||||
| <img src="https://github.com/OHIF/Viewers/blob/master/platform/docs/docs/assets/img/microscopy.webp?raw=true" alt="microscopy" width="350"/> | Slide Microscopy | [Demo](https://viewer.ohif.org/microscopy?StudyInstanceUIDs=2.25.141277760791347900862109212450152067508) |
|
||||
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 91 KiB |
@ -91,7 +91,7 @@ Let's look at one of the evaluators (for `evaluate.cornerstoneTool`)
|
||||
return;
|
||||
}
|
||||
|
||||
const toolName = getToolNameForButton(button);
|
||||
const toolName = toolbarService.getToolNameForButton(button);
|
||||
|
||||
if (!toolGroup || !toolGroup.hasTool(toolName)) {
|
||||
return {
|
||||
@ -405,7 +405,12 @@ state will get synchronized with the toolbar service automatically.
|
||||
Your toolbox toolbar buttons can have options, this is really useful
|
||||
for advanced tools that require to change some parameters. For example, the brush tool that requires the brush size to change or the mode (2D or 3D).
|
||||
|
||||
currently we support three types of options
|
||||
:::note
|
||||
Toolbox with options will run the options commands
|
||||
on the mount of the toolbox component. This is useful for setting the initial state of the toolbox.
|
||||
:::
|
||||
|
||||
Currently we support three types of options.
|
||||
|
||||
### Radio option
|
||||
|
||||
@ -423,7 +428,6 @@ three different modes
|
||||
toolNames: ['CircleScissor', 'SphereScissor', 'RectangleScissor'],
|
||||
},
|
||||
icon: 'icon-tool-shape',
|
||||
commands: _createSetToolActiveCommands('CircleScissor'),
|
||||
options: [
|
||||
{
|
||||
name: 'Shape',
|
||||
@ -456,7 +460,6 @@ We use this for brush radius change
|
||||
toolNames: ['CircularBrush', 'SphereBrush'],
|
||||
disabledText: 'Create new segmentation to enable this tool.',
|
||||
},
|
||||
commands: _createSetToolActiveCommands('CircularBrush'),
|
||||
options: [
|
||||
{
|
||||
name: 'Radius (mm)',
|
||||
|
||||
@ -160,3 +160,23 @@ const _initMeasurementService = (MeasurementService, DisplaySetService) => {
|
||||
return csToolsVer4MeasurementSource;
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
## Auto complete
|
||||
Use a customization service to add more customizations for measurement labels. Later, when adding a measurement, the user will be prompted to choose from a list of labels.
|
||||
|
||||
```js
|
||||
customizationService.addModeCustomizations([
|
||||
{
|
||||
id: 'measurementLabels',
|
||||
labelOnMeasure: true,
|
||||
exclusive: true,
|
||||
items: [
|
||||
{ value: 'Head', label: 'Head' },
|
||||
{ value: 'Neck', label: 'Neck' },
|
||||
{ value: 'Knee', label: 'Knee' },
|
||||
{ value: 'Toe', label: 'Toe' },
|
||||
],
|
||||
},
|
||||
]);
|
||||
```
|
||||
|
||||
20
platform/ui/src/assets/icons/ohif-logo-color-darkbg.svg
Normal file
20
platform/ui/src/assets/icons/ohif-logo-color-darkbg.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 26 KiB |
@ -103,7 +103,7 @@ const renderDoubleRangeSetting = option => {
|
||||
key={option.id}
|
||||
>
|
||||
<InputDoubleRange
|
||||
values={option.values}
|
||||
values={option.value}
|
||||
onChange={option.commands}
|
||||
minValue={option.min}
|
||||
maxValue={option.max}
|
||||
|
||||
@ -34,6 +34,7 @@ import link from './../../assets/icons/tool-stack-image-sync.svg';
|
||||
import listBullets from './../../assets/icons/list-bullets.svg';
|
||||
import lock from './../../assets/icons/lock.svg';
|
||||
import logoOhifSmall from './../../assets/icons/logo-ohif-small.svg';
|
||||
import logoDarkBackGround from './../../assets/icons/ohif-logo-color-darkbg.svg';
|
||||
import magnifier from './../../assets/icons/magnifier.svg';
|
||||
import notificationwarningDiamond from './../../assets/icons/notificationwarning-diamond.svg';
|
||||
import pencil from './../../assets/icons/pencil.svg';
|
||||
@ -264,6 +265,7 @@ const ICONS = {
|
||||
'list-bullets': listBullets,
|
||||
lock: lock,
|
||||
'logo-ohif-small': logoOhifSmall,
|
||||
'logo-dark-background': logoDarkBackGround,
|
||||
magnifier: magnifier,
|
||||
exclamation: exclamation,
|
||||
'notificationwarning-diamond': notificationwarningDiamond,
|
||||
|
||||
@ -21,6 +21,7 @@ function Toolbox({ servicesManager, buttonSectionId, commandsManager, title, ...
|
||||
});
|
||||
|
||||
const prevButtonIdsRef = useRef();
|
||||
const prevToolboxStateRef = useRef();
|
||||
|
||||
useEffect(() => {
|
||||
const currentButtonIdsStr = JSON.stringify(
|
||||
@ -33,11 +34,24 @@ function Toolbox({ servicesManager, buttonSectionId, commandsManager, title, ...
|
||||
})
|
||||
);
|
||||
|
||||
if (prevButtonIdsRef.current === currentButtonIdsStr) {
|
||||
const currentToolBoxStateStr = JSON.stringify(
|
||||
Object.keys(toolboxState.toolOptions).map(tool => {
|
||||
const options = toolboxState.toolOptions[tool];
|
||||
if (Array.isArray(options)) {
|
||||
return options?.map(option => `${option.id}-${option.value}`);
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
if (
|
||||
prevButtonIdsRef.current === currentButtonIdsStr &&
|
||||
prevToolboxStateRef.current === currentToolBoxStateStr
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
prevButtonIdsRef.current = currentButtonIdsStr;
|
||||
prevToolboxStateRef.current = currentToolBoxStateStr;
|
||||
|
||||
const initializeOptionsWithEnhancements = toolbarButtons.reduce(
|
||||
(accumulator, toolbarButton) => {
|
||||
@ -51,11 +65,15 @@ function Toolbox({ servicesManager, buttonSectionId, commandsManager, title, ...
|
||||
return option;
|
||||
}
|
||||
|
||||
const value =
|
||||
toolboxState.toolOptions?.[parentId]?.find(prop => prop.id === option.id)?.value ??
|
||||
option.value;
|
||||
|
||||
const updatedOptions = toolboxState.toolOptions?.[parentId];
|
||||
|
||||
return {
|
||||
...option,
|
||||
value:
|
||||
toolboxState.toolOptions?.[parentId]?.find(prop => prop.id === option.id)?.value ??
|
||||
option.value,
|
||||
value,
|
||||
commands: value => {
|
||||
api.handleToolOptionChange(parentId, option.id, value);
|
||||
|
||||
@ -72,10 +90,15 @@ function Toolbox({ servicesManager, buttonSectionId, commandsManager, title, ...
|
||||
} else if (isObject) {
|
||||
commandsManager.run({
|
||||
...command,
|
||||
commandOptions: { ...command.commandOptions, ...option, value },
|
||||
commandOptions: {
|
||||
...command.commandOptions,
|
||||
...option,
|
||||
value,
|
||||
options: updatedOptions,
|
||||
},
|
||||
});
|
||||
} else if (isFunction) {
|
||||
command({ value, commandsManager, servicesManager });
|
||||
command({ value, commandsManager, servicesManager, options: updatedOptions });
|
||||
}
|
||||
});
|
||||
},
|
||||
@ -101,7 +124,7 @@ function Toolbox({ servicesManager, buttonSectionId, commandsManager, title, ...
|
||||
);
|
||||
|
||||
api.initializeToolOptions(initializeOptionsWithEnhancements);
|
||||
}, [toolbarButtons, api]);
|
||||
}, [toolbarButtons, api, toolboxState]);
|
||||
|
||||
const handleToolOptionChange = (toolName, optionName, newValue) => {
|
||||
api.handleToolOptionChange(toolName, optionName, newValue);
|
||||
|
||||
@ -1,9 +1,17 @@
|
||||
import React from 'react';
|
||||
import { PanelSection, ToolSettings, Tooltip } from '../../components';
|
||||
import React, { useEffect, useRef } from 'react';
|
||||
import { PanelSection, ToolSettings } from '../../components';
|
||||
import classnames from 'classnames';
|
||||
|
||||
const ItemsPerRow = 4;
|
||||
|
||||
function usePrevious(value) {
|
||||
const ref = useRef();
|
||||
useEffect(() => {
|
||||
ref.current = value;
|
||||
});
|
||||
return ref.current;
|
||||
}
|
||||
|
||||
/**
|
||||
* Just refactoring from the toolbox component to make it more readable
|
||||
*/
|
||||
@ -18,6 +26,27 @@ function ToolboxUI(props) {
|
||||
useCollapsedPanel = true,
|
||||
} = props;
|
||||
|
||||
const prevToolOptions = usePrevious(activeToolOptions);
|
||||
|
||||
useEffect(() => {
|
||||
if (!activeToolOptions) {
|
||||
return;
|
||||
}
|
||||
|
||||
activeToolOptions.forEach((option, index) => {
|
||||
const prevOption = prevToolOptions ? prevToolOptions[index] : undefined;
|
||||
if (!prevOption || option.value !== prevOption.value) {
|
||||
const isOptionValid = option.condition
|
||||
? option.condition({ options: activeToolOptions })
|
||||
: true;
|
||||
if (isOptionValid) {
|
||||
const { commands } = option;
|
||||
commands(option.value);
|
||||
}
|
||||
}
|
||||
});
|
||||
}, [activeToolOptions]);
|
||||
|
||||
const render = () => {
|
||||
return (
|
||||
<>
|
||||
@ -34,7 +63,8 @@ function ToolboxUI(props) {
|
||||
const toolClasses = `ml-1 ${isLastRow ? '' : 'mb-2'}`;
|
||||
|
||||
const onInteraction = ({ itemId, id, commands }) => {
|
||||
handleToolSelect(itemId || id);
|
||||
const idToUse = itemId || id;
|
||||
handleToolSelect(idToUse);
|
||||
props.onInteraction({
|
||||
itemId,
|
||||
commands,
|
||||
|
||||
@ -78,6 +78,13 @@ const Tooltip = ({
|
||||
handleMouseOutDebounced();
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
handleMouseOverDebounced.cancel();
|
||||
handleMouseOutDebounced.cancel();
|
||||
};
|
||||
}, [handleMouseOverDebounced, handleMouseOutDebounced]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isOpen && onHide) {
|
||||
onHide();
|
||||
|
||||
58
yarn.lock
58
yarn.lock
@ -1497,13 +1497,13 @@
|
||||
resolved "https://registry.yarnpkg.com/@colors/colors/-/colors-1.5.0.tgz#bb504579c1cae923e6576a4f5da43d25f97bdbd9"
|
||||
integrity sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==
|
||||
|
||||
"@cornerstonejs/adapters@^1.70.13":
|
||||
version "1.70.13"
|
||||
resolved "https://registry.yarnpkg.com/@cornerstonejs/adapters/-/adapters-1.70.13.tgz#195023f5600272f6ee70090b3c61060d13b9cec7"
|
||||
integrity sha512-PSitPx/A5aP0VhVTC72trwf3CfVGAXYvcAfAvuk3WzSONhCJ6cg5Xsxpl0NtEt7prsT3u60Bgr94Ea+9VJVdog==
|
||||
"@cornerstonejs/adapters@^1.70.14":
|
||||
version "1.70.14"
|
||||
resolved "https://registry.yarnpkg.com/@cornerstonejs/adapters/-/adapters-1.70.14.tgz#1eba07366a66f4d261f3a1e3769260ccf74660af"
|
||||
integrity sha512-jqMIpvqDH1CPO6rPa7P9CtINOOinwfZpPU6UAuoV8zSJtwOpwViP0Xp9rx9smSmzCc3l3ZZcQ9Wq1DnuzpgPJw==
|
||||
dependencies:
|
||||
"@babel/runtime-corejs2" "^7.17.8"
|
||||
"@cornerstonejs/tools" "^1.70.13"
|
||||
"@cornerstonejs/tools" "^1.70.14"
|
||||
buffer "^6.0.3"
|
||||
dcmjs "^0.29.8"
|
||||
gl-matrix "^3.4.3"
|
||||
@ -1550,45 +1550,45 @@
|
||||
resolved "https://registry.yarnpkg.com/@cornerstonejs/codec-openjph/-/codec-openjph-2.4.5.tgz#8690b61a86fa53ef38a70eee9d665a79229517c0"
|
||||
integrity sha512-MZCUy8VG0VG5Nl1l58+g+kH3LujAzLYTfJqkwpWI2gjSrGXnP6lgwyy4GmPRZWVoS40/B1LDNALK905cNWm+sg==
|
||||
|
||||
"@cornerstonejs/core@^1.70.13":
|
||||
version "1.70.13"
|
||||
resolved "https://registry.yarnpkg.com/@cornerstonejs/core/-/core-1.70.13.tgz#a1976588b93461e2350866ecd761660ee1b74c8d"
|
||||
integrity sha512-Bi+ErK6rHNx46CVQiUiw/00ipOWdy7EdyC4U0S30oiLE1Jz1PegIniVDogleXtO1jDkTkNnuWnPCoNhpUgNNuQ==
|
||||
"@cornerstonejs/core@^1.70.14":
|
||||
version "1.70.14"
|
||||
resolved "https://registry.yarnpkg.com/@cornerstonejs/core/-/core-1.70.14.tgz#f19c9c3da793513b0f6163661f98c633a83ee2b9"
|
||||
integrity sha512-DrhjlbtGiV9EyHUiGh2RhPzhFQWxZwDCqqkhxBOLl5QiUalByZNyZyN1OHtX0zOyIwVsFw58yvVKYX7s3bpqOw==
|
||||
dependencies:
|
||||
"@kitware/vtk.js" "30.3.3"
|
||||
"@kitware/vtk.js" "30.4.1"
|
||||
comlink "^4.4.1"
|
||||
detect-gpu "^5.0.22"
|
||||
gl-matrix "^3.4.3"
|
||||
lodash.clonedeep "4.5.0"
|
||||
|
||||
"@cornerstonejs/dicom-image-loader@^1.70.13":
|
||||
version "1.70.13"
|
||||
resolved "https://registry.yarnpkg.com/@cornerstonejs/dicom-image-loader/-/dicom-image-loader-1.70.13.tgz#cf2aea8b6e538b6f005106ef6401869abde226d2"
|
||||
integrity sha512-PXe9/xmv5vq1raREL9qsD3Ci68pWwCpjVxmyIigpV7lOdtziivvMVNyNSDZCjexBpbsB5a0fKcZG4jl82Jz1HA==
|
||||
"@cornerstonejs/dicom-image-loader@^1.70.14":
|
||||
version "1.70.14"
|
||||
resolved "https://registry.yarnpkg.com/@cornerstonejs/dicom-image-loader/-/dicom-image-loader-1.70.14.tgz#3f776bc92b627ec1a80aec0581ee40d06f1ff815"
|
||||
integrity sha512-bpYf9s/jhTegxCrrgvvtfJoFDOltkEWAKxqaUjt01g7Z6lnkC6IUaH3XoMZy/0n4un28tTk3xVHF1vAw0upCig==
|
||||
dependencies:
|
||||
"@cornerstonejs/codec-charls" "^1.2.3"
|
||||
"@cornerstonejs/codec-libjpeg-turbo-8bit" "^1.2.2"
|
||||
"@cornerstonejs/codec-openjpeg" "^1.2.2"
|
||||
"@cornerstonejs/codec-openjph" "^2.4.5"
|
||||
"@cornerstonejs/core" "^1.70.13"
|
||||
"@cornerstonejs/core" "^1.70.14"
|
||||
dicom-parser "^1.8.9"
|
||||
pako "^2.0.4"
|
||||
uuid "^9.0.0"
|
||||
|
||||
"@cornerstonejs/streaming-image-volume-loader@^1.70.13":
|
||||
version "1.70.13"
|
||||
resolved "https://registry.yarnpkg.com/@cornerstonejs/streaming-image-volume-loader/-/streaming-image-volume-loader-1.70.13.tgz#77a3751ddc5ab17cff050007c7e3d7e0b8c58c13"
|
||||
integrity sha512-Pd90y2JFbQ4LriRkkYXXvD3mhIWyiVdGDznhrJN/8ZC+4m5XusOlvOk2Ia/85e+WAgIUlHyDCgOuzewG8of+6w==
|
||||
"@cornerstonejs/streaming-image-volume-loader@^1.70.14":
|
||||
version "1.70.14"
|
||||
resolved "https://registry.yarnpkg.com/@cornerstonejs/streaming-image-volume-loader/-/streaming-image-volume-loader-1.70.14.tgz#e46a4152f6b5ac9a1ce7079d765b8ce9bdcc91ef"
|
||||
integrity sha512-yObvU8CB/2F0gLyeRzM5NIxNLQimNHEfJnXuv/EYNATq+wOeKWle3jfUm9xN/ZTiSp1MrBMaG7g26OlyIjEp4Q==
|
||||
dependencies:
|
||||
"@cornerstonejs/core" "^1.70.13"
|
||||
"@cornerstonejs/core" "^1.70.14"
|
||||
comlink "^4.4.1"
|
||||
|
||||
"@cornerstonejs/tools@^1.70.13":
|
||||
version "1.70.13"
|
||||
resolved "https://registry.yarnpkg.com/@cornerstonejs/tools/-/tools-1.70.13.tgz#fca6abc331841a2ac1a4403b5522cdcc6cacc708"
|
||||
integrity sha512-Eza/4A2p51YPqZbvP6HwZC0yJCkogsda39aB+Eh5VTPQSqHn+VCBmZ9U9Qi9+0cpaye53A5OhjXAOoR6pZlqRQ==
|
||||
"@cornerstonejs/tools@^1.70.14":
|
||||
version "1.70.14"
|
||||
resolved "https://registry.yarnpkg.com/@cornerstonejs/tools/-/tools-1.70.14.tgz#7083459c3e206c280cf728d44fe9f4ca09874d2b"
|
||||
integrity sha512-NI4yLC9PNHwa2KO0DqjZET2vZz8VfW095m637gk5E5wojq3dvSJot7w4y+zTCiyaxXjUUoMzc0TJYmmqtdbWPg==
|
||||
dependencies:
|
||||
"@cornerstonejs/core" "^1.70.13"
|
||||
"@cornerstonejs/core" "^1.70.14"
|
||||
"@icr/polyseg-wasm" "0.4.0"
|
||||
"@types/offscreencanvas" "2019.7.3"
|
||||
comlink "^4.4.1"
|
||||
@ -2844,10 +2844,10 @@
|
||||
resolved "https://registry.yarnpkg.com/@juggle/resize-observer/-/resize-observer-3.4.0.tgz#08d6c5e20cf7e4cc02fd181c4b0c225cd31dbb60"
|
||||
integrity sha512-dfLbk+PwWvFzSxwk3n5ySL0hfBog779o8h68wK/7/APo/7cgyWp5jcXockbxdk5kFRkbeXWm4Fbi9FrdN381sA==
|
||||
|
||||
"@kitware/vtk.js@30.3.3":
|
||||
version "30.3.3"
|
||||
resolved "https://registry.yarnpkg.com/@kitware/vtk.js/-/vtk.js-30.3.3.tgz#348e17fdc896c912eca7036f607d21ef6128bca1"
|
||||
integrity sha512-es9I5LLlg+TpaIXk5aHSPzyI/YnCI4egHA1cbG98IP7t9W4KODUcJjyrXFAa7aSvfXZ8y2jhD9qrsaEnstEkJA==
|
||||
"@kitware/vtk.js@30.4.1":
|
||||
version "30.4.1"
|
||||
resolved "https://registry.yarnpkg.com/@kitware/vtk.js/-/vtk.js-30.4.1.tgz#ce8a50012e56341d2d01708a32a2ac3afa675b67"
|
||||
integrity sha512-jBJFm8AyWpJjNFFBadXyvBwegdD9M6WRdxmIb+x/MVpCyA5lEZSMemhiMn71oKsznaEe5Pjv2VDVJWmwK0vhUg==
|
||||
dependencies:
|
||||
"@babel/runtime" "7.22.11"
|
||||
"@types/webxr" "^0.5.5"
|
||||
|
||||
Loading…
Reference in New Issue
Block a user