feat(layout): new layout selector with 3D volume rendering (#3923)

This commit is contained in:
Ibrahim 2024-03-28 10:40:32 -04:00 committed by GitHub
parent 6f610b09e1
commit 617043fe0d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
51 changed files with 829 additions and 239 deletions

View File

@ -180,7 +180,8 @@ function getUpdatedViewportsForSegmentation({
});
}
});
return updatedViewports;
return updatedViewports.filter(v => v.viewportOptions?.viewportType !== 'volume3d');
}
export {

View File

@ -311,6 +311,10 @@ function commandsModule({
return;
}
if (!toolGroup.hasTool(toolName)) {
return;
}
const activeToolName = toolGroup.getActivePrimaryMouseButtonTool();
if (activeToolName) {

View File

@ -166,6 +166,19 @@ export default function getToolbarModule({ commandsManager, servicesManager }) {
};
},
},
{
name: 'evaluate.not3D',
evaluate: ({ viewportId, button }) => {
const viewport = cornerstoneViewportService.getCornerstoneViewport(viewportId);
if (viewport?.type === 'volume3d') {
return {
disabled: true,
className: '!text-common-bright ohif-disabled',
};
}
},
},
{
name: 'evaluate.viewportProperties.toggle',
evaluate: ({ viewportId, button }) => {

View File

@ -1,7 +1,9 @@
export const fourUp = {
id: 'fourUp',
locked: true,
name: 'fourUp',
name: '3D four up',
icon: 'layout-advanced-3d-four-up',
isPreset: true,
createdDate: '2023-03-15T10:29:44.894Z',
modifiedDate: '2023-03-15T10:29:44.894Z',
availableTo: {},

View File

@ -1,7 +1,9 @@
export const main3D = {
id: 'main3D',
locked: true,
name: 'main3D',
name: '3D main',
icon: 'layout-advanced-3d-main',
isPreset: true,
createdDate: '2023-03-15T10:29:44.894Z',
modifiedDate: '2023-03-15T10:29:44.894Z',
availableTo: {},

View File

@ -2,8 +2,10 @@ import { Types } from '@ohif/core';
export const mpr: Types.HangingProtocol.Protocol = {
id: 'mpr',
name: 'Multi-Planar Reconstruction',
name: 'MPR',
locked: true,
icon: 'layout-advanced-mpr',
isPreset: true,
createdDate: '2021-02-23',
modifiedDate: '2023-08-15',
availableTo: {},

View File

@ -1,7 +1,9 @@
export const only3D = {
id: 'only3D',
locked: true,
name: 'only3D',
name: '3D only',
icon: 'layout-advanced-3d-only',
isPreset: true,
createdDate: '2023-03-15T10:29:44.894Z',
modifiedDate: '2023-03-15T10:29:44.894Z',
availableTo: {},

View File

@ -1,7 +1,9 @@
export const primary3D = {
id: 'primary3D',
locked: true,
name: 'primary3D',
name: '3D primary',
icon: 'layout-advanced-3d-primary',
isPreset: true,
createdDate: '2023-03-15T10:29:44.894Z',
modifiedDate: '2023-03-15T10:29:44.894Z',
availableTo: {},

View File

@ -1,7 +1,9 @@
export const primaryAxial = {
id: 'primaryAxial',
locked: true,
name: 'primaryAxial',
name: 'Axial Primary',
icon: 'layout-advanced-axial-primary',
isPreset: true,
createdDate: '2023-03-15T10:29:44.894Z',
modifiedDate: '2023-03-15T10:29:44.894Z',
availableTo: {},

View File

@ -0,0 +1,88 @@
import React, { useEffect, useState, useCallback } from 'react';
import PropTypes from 'prop-types';
import { LayoutSelector as OHIFLayoutSelector, ToolbarButton } from '@ohif/ui';
import { ServicesManager } from '@ohif/core';
function LegacyLayoutSelectorWithServices({ servicesManager, ...props }) {
const { toolbarService } = servicesManager.services;
const onSelection = useCallback(
props => {
toolbarService.recordInteraction({
interactionType: 'action',
commands: [
{
commandName: 'setViewportGridLayout',
commandOptions: { ...props },
context: 'DEFAULT',
},
],
});
},
[toolbarService]
);
return (
<LayoutSelector
{...props}
onSelection={onSelection}
/>
);
}
function LayoutSelector({ rows, columns, className, onSelection, ...rest }) {
const [isOpen, setIsOpen] = useState(false);
const closeOnOutsideClick = () => {
if (isOpen) {
setIsOpen(false);
}
};
useEffect(() => {
window.addEventListener('click', closeOnOutsideClick);
return () => {
window.removeEventListener('click', closeOnOutsideClick);
};
}, [isOpen]);
const onInteractionHandler = () => setIsOpen(!isOpen);
const DropdownContent = isOpen ? OHIFLayoutSelector : null;
return (
<ToolbarButton
id="Layout"
label="Grid Layout"
icon="tool-layout"
onInteraction={onInteractionHandler}
className={className}
rounded={rest.rounded}
dropdownContent={
DropdownContent !== null && (
<DropdownContent
rows={rows}
columns={columns}
onSelection={onSelection}
/>
)
}
isActive={isOpen}
type="toggle"
/>
);
}
LayoutSelector.propTypes = {
rows: PropTypes.number,
columns: PropTypes.number,
onLayoutChange: PropTypes.func,
servicesManager: PropTypes.instanceOf(ServicesManager),
};
LayoutSelector.defaultProps = {
rows: 3,
columns: 3,
onLayoutChange: () => {},
};
export default LegacyLayoutSelectorWithServices;

View File

@ -1,37 +1,127 @@
import React, { useEffect, useState, useCallback } from 'react';
import PropTypes from 'prop-types';
import { LayoutSelector as OHIFLayoutSelector, ToolbarButton } from '@ohif/ui';
import { LayoutSelector as OHIFLayoutSelector, ToolbarButton, LayoutPreset } from '@ohif/ui';
import { ServicesManager } from '@ohif/core';
function ToolbarLayoutSelectorWithServices({ servicesManager, commands, ...props }) {
const { toolbarService, viewportGridService } = servicesManager.services;
const defaultCommonPresets = [
{
icon: 'layout-common-1x1',
commandOptions: {
numRows: 1,
numCols: 1,
},
},
{
icon: 'layout-common-1x2',
commandOptions: {
numRows: 1,
numCols: 2,
},
},
{
icon: 'layout-common-2x2',
commandOptions: {
numRows: 2,
numCols: 2,
},
},
{
icon: 'layout-common-2x3',
commandOptions: {
numRows: 2,
numCols: 3,
},
},
];
const onInteraction = useCallback(
args => {
const viewportId = viewportGridService.getActiveViewportId();
const refreshProps = {
viewportId,
};
if (props.onLayoutChange) {
props.onLayoutChange(args);
const generateAdvancedPresets = hangingProtocolService => {
const hangingProtocols = Array.from(hangingProtocolService.protocols.values());
return hangingProtocols
.map(hp => {
if (!hp.isPreset) {
return null;
}
toolbarService.recordInteraction({ commands }, { ...args, refreshProps });
return {
icon: hp.icon,
title: hp.name,
commandOptions: {
protocolId: hp.id,
},
};
})
.filter(preset => preset !== null);
};
function ToolbarLayoutSelectorWithServices({ servicesManager, ...props }) {
const { toolbarService } = servicesManager.services;
const [isDisabled, setIsDisabled] = useState(false);
const handleMouseEnter = () => {
setIsDisabled(false);
};
const onSelection = useCallback(
props => {
toolbarService.recordInteraction({
interactionType: 'action',
commands: [
{
commandName: 'setViewportGridLayout',
commandOptions: { ...props },
},
],
});
setIsDisabled(true);
},
[toolbarService]
);
const onSelectionPreset = useCallback(
props => {
toolbarService.recordInteraction({
interactionType: 'action',
commands: [
{
commandName: 'setHangingProtocol',
commandOptions: { ...props },
},
],
});
setIsDisabled(true);
},
[toolbarService]
);
return (
<LayoutSelector
{...props}
onInteraction={onInteraction}
/>
<div onMouseEnter={handleMouseEnter}>
<LayoutSelector
{...props}
onSelection={onSelection}
onSelectionPreset={onSelectionPreset}
servicesManager={servicesManager}
tooltipDisabled={isDisabled}
/>
</div>
);
}
function LayoutSelector({ rows, columns, className, onInteraction, ...rest }) {
function LayoutSelector({
rows,
columns,
className,
onSelection,
onSelectionPreset,
servicesManager,
tooltipDisabled,
...rest
}) {
const [isOpen, setIsOpen] = useState(false);
const { customizationService, hangingProtocolService } = servicesManager.services;
const commonPresets = customizationService.get('commonPresets') || defaultCommonPresets;
const advancedPresets =
customizationService.get('advancedPresets') || generateAdvancedPresets(hangingProtocolService);
const closeOnOutsideClick = () => {
if (isOpen) {
setIsOpen(false);
@ -45,24 +135,68 @@ function LayoutSelector({ rows, columns, className, onInteraction, ...rest }) {
};
}, [isOpen]);
const onInteractionHandler = () => setIsOpen(!isOpen);
const onInteractionHandler = () => {
setIsOpen(!isOpen);
};
const DropdownContent = isOpen ? OHIFLayoutSelector : null;
return (
<ToolbarButton
id="Layout"
label="Grid Layout"
label="Layout"
icon="tool-layout"
onInteraction={onInteractionHandler}
className={className}
rounded={rest.rounded}
disableToolTip={tooltipDisabled}
dropdownContent={
DropdownContent !== null && (
<DropdownContent
rows={rows}
columns={columns}
onSelection={onInteraction}
/>
<div className="flex">
<div className="bg-secondary-dark flex flex-col gap-2.5 p-2">
<div className="text-aqua-pale text-xs">Common</div>
<div className="flex gap-4">
{commonPresets.map((preset, index) => (
<LayoutPreset
key={index}
classNames="hover:bg-primary-dark group p-1"
icon={preset.icon}
commandOptions={preset.commandOptions}
onSelection={onSelection}
/>
))}
</div>
<div className="h-[2px] bg-black"></div>
<div className="text-aqua-pale text-xs">Advanced</div>
<div className="flex flex-col gap-2.5">
{advancedPresets.map((preset, index) => (
<LayoutPreset
key={index + commonPresets.length}
classNames="hover:bg-primary-dark group flex gap-2 p-1"
icon={preset.icon}
title={preset.title}
commandOptions={preset.commandOptions}
onSelection={onSelectionPreset}
/>
))}
</div>
</div>
<div className="bg-primary-dark flex flex-col gap-2.5 border-l-2 border-solid border-black p-2">
<div className="text-aqua-pale text-xs">Custom</div>
<DropdownContent
rows={rows}
columns={columns}
onSelection={onSelection}
/>
<p className="text-aqua-pale text-xs leading-tight">
Hover to select <br></br>rows and columns <br></br> Click to apply
</p>
</div>
</div>
)
}
isActive={isOpen}
@ -79,8 +213,8 @@ LayoutSelector.propTypes = {
};
LayoutSelector.defaultProps = {
columns: 4,
rows: 3,
columns: 3,
onLayoutChange: () => {},
};

View File

@ -148,7 +148,7 @@ const toolbarButtons: Button[] = [
uiType: 'ohif.layoutSelector',
props: {
rows: 3,
columns: 3,
columns: 4,
evaluate: 'evaluate.action',
commands: [
{

View File

@ -193,10 +193,38 @@ function initMPRToolGroup(extensionManager, toolGroupService, commandsManager) {
toolGroupService.createToolGroupAndAddTools('mpr', tools);
}
function initVolume3DToolGroup(extensionManager, toolGroupService) {
const utilityModule = extensionManager.getModuleEntry(
'@ohif/extension-cornerstone.utilityModule.tools'
);
const { toolNames, Enums } = utilityModule.exports;
const tools = {
active: [
{
toolName: toolNames.TrackballRotateTool,
bindings: [{ mouseButton: Enums.MouseBindings.Primary }],
},
{
toolName: toolNames.Zoom,
bindings: [{ mouseButton: Enums.MouseBindings.Secondary }],
},
{
toolName: toolNames.Pan,
bindings: [{ mouseButton: Enums.MouseBindings.Auxiliary }],
},
],
};
toolGroupService.createToolGroupAndAddTools('volume3d', tools);
}
function initToolGroups(extensionManager, toolGroupService, commandsManager) {
initDefaultToolGroup(extensionManager, toolGroupService, commandsManager, 'default');
initSRToolGroup(extensionManager, toolGroupService, commandsManager);
initMPRToolGroup(extensionManager, toolGroupService, commandsManager);
initVolume3DToolGroup(extensionManager, toolGroupService);
}
export default initToolGroups;

View File

@ -155,6 +155,41 @@ const toolbarButtons: Button[] = [
evaluate: 'evaluate.cornerstoneTool',
},
},
{
id: 'MPR',
uiType: 'ohif.radioGroup',
props: {
icon: 'icon-mpr',
label: 'MPR',
commands: [
{
commandName: 'toggleHangingProtocol',
commandOptions: {
protocolId: 'mpr',
},
},
],
evaluate: 'evaluate.mpr',
},
},
{
id: 'TrackBallRotate',
type: 'ohif.radioGroup',
props: {
type: 'tool',
icon: 'tool-3d-rotate',
label: '3D Rotate',
commands: [
{
commandName: 'setToolActive',
commandOptions: {
toolName: 'TrackBallRotate',
},
context: 'CORNERSTONE',
},
],
},
},
{
id: 'Capture',
uiType: 'ohif.radioGroup',
@ -174,30 +209,9 @@ const toolbarButtons: Button[] = [
uiType: 'ohif.layoutSelector',
props: {
rows: 3,
columns: 3,
columns: 4,
evaluate: 'evaluate.action',
commands: [
{
commandName: 'setViewportGridLayout',
},
],
},
},
{
id: 'MPR',
uiType: 'ohif.radioGroup',
props: {
icon: 'icon-mpr',
label: 'MPR',
commands: [
{
commandName: 'toggleHangingProtocol',
commandOptions: {
protocolId: 'mpr',
},
},
],
evaluate: 'evaluate.mpr',
commands: 'setViewportGridLayout',
},
},
{

View File

@ -83,11 +83,11 @@ function modeFactory({ modeConfiguration }) {
toolbarService.createButtonSection('primary', [
'MeasurementTools',
'Zoom',
'WindowLevel',
'Pan',
'TrackballRotate',
'WindowLevel',
'Capture',
'Layout',
'MPR',
'Crosshairs',
'MoreTools',
]);

View File

@ -54,7 +54,7 @@ const moreTools = [
label: 'Flip Horizontal',
tooltip: 'Flip Horizontally',
commands: 'flipViewportHorizontal',
evaluate: 'evaluate.viewportProperties.toggle',
evaluate: ['evaluate.viewportProperties.toggle', 'evaluate.not3D'],
}),
createButton({
id: 'ImageSliceSync',
@ -73,7 +73,7 @@ const moreTools = [
commandOptions: { toggledState: true },
},
},
evaluate: 'evaluate.cornerstone.synchronizer',
evaluate: ['evaluate.cornerstone.synchronizer', 'evaluate.not3D'],
}),
createButton({
id: 'ReferenceLines',
@ -137,7 +137,7 @@ const moreTools = [
label: 'Cine',
tooltip: 'Cine',
commands: 'toggleCine',
evaluate: 'evaluate.cine',
evaluate: ['evaluate.cine', 'evaluate.not3D'],
}),
createButton({
id: 'Angle',

View File

@ -1,9 +1,6 @@
// TODO: torn, can either bake this here; or have to create a whole new button type
// Only ways that you can pass in a custom React component for render :l
import {
// ListMenu,
WindowLevelMenuItem,
} from '@ohif/ui';
import { WindowLevelMenuItem } from '@ohif/ui';
import { defaults, ToolbarService } from '@ohif/core';
import type { Button } from '@ohif/core/types';
@ -36,7 +33,7 @@ function _createWwwcPreset(preset, title, subtitle) {
export const setToolActiveToolbar = {
commandName: 'setToolActiveToolbar',
commandOptions: {
toolGroupIds: ['default', 'mpr', 'SRToolGroup'],
toolGroupIds: ['default', 'mpr', 'SRToolGroup', 'volume3d'],
},
};
@ -155,6 +152,17 @@ const toolbarButtons: Button[] = [
evaluate: 'evaluate.cornerstoneTool',
},
},
{
id: 'TrackballRotate',
uiType: 'ohif.radioGroup',
props: {
type: 'tool',
icon: 'tool-3d-rotate',
label: '3D Rotate',
commands: setToolActiveToolbar,
evaluate: 'evaluate.cornerstoneTool',
},
},
{
id: 'Capture',
uiType: 'ohif.radioGroup',
@ -174,7 +182,7 @@ const toolbarButtons: Button[] = [
uiType: 'ohif.layoutSelector',
props: {
rows: 3,
columns: 3,
columns: 4,
evaluate: 'evaluate.action',
commands: [
{
@ -183,23 +191,6 @@ const toolbarButtons: Button[] = [
],
},
},
{
id: 'MPR',
uiType: 'ohif.radioGroup',
props: {
icon: 'icon-mpr',
label: 'MPR',
commands: [
{
commandName: 'toggleHangingProtocol',
commandOptions: {
protocolId: 'mpr',
},
},
],
evaluate: 'evaluate.mpr',
},
},
{
id: 'Crosshairs',
uiType: 'ohif.radioGroup',

View File

@ -35,7 +35,14 @@
"test:unit:ci": "jest --ci --runInBand --collectCoverage --passWithNoTests"
},
"peerDependencies": {
"@ohif/core": "3.8.0-beta.64"
"@ohif/core": "3.8.0-beta.64",
"@ohif/extension-cornerstone": "3.8.0-beta.64",
"@ohif/extension-cornerstone-dicom-rt": "3.8.0-beta.64",
"@ohif/extension-cornerstone-dicom-seg": "3.8.0-beta.64",
"@ohif/extension-cornerstone-dicom-sr": "3.8.0-beta.64",
"@ohif/extension-default": "3.8.0-beta.64",
"@ohif/extension-dicom-pdf": "3.8.0-beta.64",
"@ohif/extension-dicom-video": "3.8.0-beta.64"
},
"dependencies": {
"@babel/runtime": "^7.20.13",

View File

@ -4,6 +4,9 @@ import toolbarButtons from './toolbarButtons';
import segmentationButtons from './segmentationButtons';
import initToolGroups from './initToolGroups';
const DEFAULT_TOOL_GROUP_ID = 'default';
const VOLUME3D_TOOL_GROUP_ID = 'volume3d';
const ohif = {
layout: '@ohif/extension-default.layoutTemplateModule.viewerLayout',
sopClassHandler: '@ohif/extension-default.sopClassHandlerModule.stack',
@ -62,12 +65,13 @@ function modeFactory({ modeConfiguration }) {
toolbarService.addButtons(segmentationButtons);
toolbarService.createButtonSection('primary', [
'WindowLevel',
'Pan',
'Zoom',
'TrackballRotate',
'Capture',
'Layout',
'MPR',
'Crosshairs',
'Zoom',
'MoreTools',
]);
toolbarService.createButtonSection('segmentationToolbox', ['BrushTools', 'Shapes']);
@ -105,9 +109,13 @@ function modeFactory({ modeConfiguration }) {
// that is not supported by the mode
const modalitiesArray = modalities.split('\\');
return {
valid: modalitiesArray.length === 1 ? !['SM', 'US', 'MG', 'OT', 'DOC', 'CR'].includes(modalitiesArray[0]) : true,
description: 'The mode does not support studies that ONLY include the following modalities: SM, US, MG, OT, DOC, CR',
}
valid:
modalitiesArray.length === 1
? !['SM', 'US', 'MG', 'OT', 'DOC', 'CR'].includes(modalitiesArray[0])
: true,
description:
'The mode does not support studies that ONLY include the following modalities: SM, US, MG, OT, DOC, CR',
};
},
/**
* Mode Routes are used to define the mode's behavior. A list of Mode Route

View File

@ -81,6 +81,7 @@ function initMPRToolGroup(extensionManager, toolGroupService, commandsManager) {
toolName: utilityModule.exports.toolNames.Crosshairs,
configuration: {
viewportIndicators: false,
disableOnPassive: true,
autoPan: {
enabled: false,
panSize: 10,
@ -92,9 +93,37 @@ function initMPRToolGroup(extensionManager, toolGroupService, commandsManager) {
toolGroupService.createToolGroupAndAddTools('mpr', tools);
}
function initVolume3DToolGroup(extensionManager, toolGroupService) {
const utilityModule = extensionManager.getModuleEntry(
'@ohif/extension-cornerstone.utilityModule.tools'
);
const { toolNames, Enums } = utilityModule.exports;
const tools = {
active: [
{
toolName: toolNames.TrackballRotateTool,
bindings: [{ mouseButton: Enums.MouseBindings.Primary }],
},
{
toolName: toolNames.Zoom,
bindings: [{ mouseButton: Enums.MouseBindings.Secondary }],
},
{
toolName: toolNames.Pan,
bindings: [{ mouseButton: Enums.MouseBindings.Auxiliary }],
},
],
};
toolGroupService.createToolGroupAndAddTools('volume3d', tools);
}
function initToolGroups(extensionManager, toolGroupService, commandsManager) {
initDefaultToolGroup(extensionManager, toolGroupService, commandsManager, 'default');
initMPRToolGroup(extensionManager, toolGroupService, commandsManager);
initVolume3DToolGroup(extensionManager, toolGroupService);
}
export default initToolGroups;

View File

@ -1,8 +1,16 @@
import { defaults, ToolbarService } from '@ohif/core';
import type { Button } from '@ohif/core/types';
import { defaults, ToolbarService, ViewportGridService } from '@ohif/core';
import { WindowLevelMenuItem } from '@ohif/ui';
const { windowLevelPresets } = defaults;
const { createButton } = ToolbarService;
const ReferenceLinesListeners: RunCommand = [
{
commandName: 'setSourceViewportForReferenceLinesTool',
context: 'CORNERSTONE',
},
];
function _createWwwcPreset(preset, title, subtitle) {
return {
@ -21,16 +29,12 @@ function _createWwwcPreset(preset, title, subtitle) {
};
}
function _createSetToolActiveCommands(toolName, toolGroupIds = ['default', 'mpr', 'SRToolGroup']) {
return toolGroupIds.map(toolGroupId => ({
commandName: 'setToolActive',
commandOptions: {
toolGroupId,
toolName,
},
context: 'CORNERSTONE',
}));
}
export const setToolActiveToolbar = {
commandName: 'setToolActiveToolbar',
commandOptions: {
toolGroupIds: ['default', 'mpr', 'SRToolGroup'],
},
};
const toolbarButtons: Button[] = [
{
@ -39,7 +43,7 @@ const toolbarButtons: Button[] = [
props: {
icon: 'tool-zoom',
label: 'Zoom',
commands: _createSetToolActiveCommands('Zoom'),
commands: setToolActiveToolbar,
evaluate: 'evaluate.cornerstoneTool',
},
},
@ -48,12 +52,12 @@ const toolbarButtons: Button[] = [
uiType: 'ohif.splitButton',
props: {
groupId: 'WindowLevel',
primary: ToolbarService.createButton({
primary: createButton({
id: 'WindowLevel',
icon: 'tool-window-level',
label: 'Window Level',
tooltip: 'Window Level',
commands: _createSetToolActiveCommands('WindowLevel'),
commands: setToolActiveToolbar,
evaluate: 'evaluate.cornerstoneTool',
}),
secondary: {
@ -76,7 +80,18 @@ const toolbarButtons: Button[] = [
props: {
icon: 'tool-move',
label: 'Pan',
commands: _createSetToolActiveCommands('Pan'),
commands: setToolActiveToolbar,
evaluate: 'evaluate.cornerstoneTool',
},
},
{
id: 'TrackballRotate',
uiType: 'ohif.radioGroup',
props: {
type: 'tool',
icon: 'tool-3d-rotate',
label: '3D Rotate',
commands: setToolActiveToolbar,
evaluate: 'evaluate.cornerstoneTool',
},
},
@ -86,12 +101,7 @@ const toolbarButtons: Button[] = [
props: {
icon: 'tool-capture',
label: 'Capture',
commands: [
{
commandName: 'showDownloadViewportModal',
context: 'CORNERSTONE',
},
],
commands: 'showDownloadViewportModal',
evaluate: 'evaluate.action',
},
},
@ -100,31 +110,9 @@ const toolbarButtons: Button[] = [
uiType: 'ohif.layoutSelector',
props: {
rows: 3,
columns: 3,
columns: 4,
evaluate: 'evaluate.action',
commands: [
{
commandName: 'setViewportGridLayout',
},
],
},
},
{
id: 'MPR',
uiType: 'ohif.radioGroup',
props: {
icon: 'icon-mpr',
label: 'MPR',
commands: [
{
commandName: 'toggleHangingProtocol',
commandOptions: {
protocolId: 'mpr',
},
context: 'DEFAULT',
},
],
evaluate: 'evaluate.mpr',
commands: 'setViewportGridLayout',
},
},
{
@ -133,7 +121,12 @@ const toolbarButtons: Button[] = [
props: {
icon: 'tool-crosshair',
label: 'Crosshairs',
commands: _createSetToolActiveCommands('Crosshairs', ['mpr']),
commands: {
commandName: 'setToolActiveToolbar',
commandOptions: {
toolGroupIds: ['mpr'],
},
},
evaluate: 'evaluate.cornerstoneTool',
},
},
@ -143,104 +136,146 @@ const toolbarButtons: Button[] = [
props: {
groupId: 'MoreTools',
evaluate: 'evaluate.group.promoteToPrimaryIfCornerstoneToolNotActiveInTheList',
primary: ToolbarService.createButton({
primary: createButton({
id: 'Reset',
icon: 'tool-reset',
label: 'Reset View',
tooltip: 'Reset',
commands: [
{
commandName: 'resetViewport',
context: 'CORNERSTONE',
},
],
tooltip: 'Reset View',
label: 'Reset',
commands: 'resetViewport',
evaluate: 'evaluate.action',
}),
secondary: {
icon: 'chevron-down',
label: '',
tooltip: 'More Tools',
},
items: [
ToolbarService.createButton({
id: 'RotateRight',
createButton({
id: 'Reset',
icon: 'tool-reset',
label: 'Reset View',
tooltip: 'Reset View',
commands: 'resetViewport',
evaluate: 'evaluate.action',
}),
createButton({
id: 'rotate-right',
icon: 'tool-rotate-right',
label: 'Rotate Right',
tooltip: 'Rotate +90',
commands: [
{
commandName: 'rotateViewportCW',
context: 'CORNERSTONE',
},
],
commands: 'rotateViewportCW',
evaluate: 'evaluate.action',
}),
ToolbarService.createButton({
id: 'FlipHorizontal',
createButton({
id: 'flipHorizontal',
icon: 'tool-flip-horizontal',
label: 'Flip Horizontally',
tooltip: 'Flip Horizontal',
commands: [
{
commandName: 'flipViewportHorizontal',
context: 'CORNERSTONE',
},
],
evaluate: 'evaluate.action',
label: 'Flip Horizontal',
tooltip: 'Flip Horizontally',
commands: 'flipViewportHorizontal',
evaluate: ['evaluate.viewportProperties.toggle', 'evaluate.not3D'],
}),
ToolbarService.createButton({
createButton({
id: 'ReferenceLines',
icon: 'tool-referenceLines',
label: 'Reference Lines',
tooltip: 'Show Reference Lines',
commands: {
commandName: 'setToolEnabled',
commandOptions: {
toolName: 'ReferenceLines',
toggle: true, // Toggle the tool on/off upon click
},
},
listeners: {
[ViewportGridService.EVENTS.ACTIVE_VIEWPORT_ID_CHANGED]: ReferenceLinesListeners,
[ViewportGridService.EVENTS.VIEWPORTS_READY]: ReferenceLinesListeners,
},
evaluate: 'evaluate.cornerstoneTool.toggle',
}),
createButton({
id: 'ImageOverlay',
icon: 'toggle-dicom-overlay',
label: 'Image Overlay',
tooltip: 'Toggle Image Overlay',
commands: {
commandName: 'setToolEnabled',
commandOptions: {
toolName: 'ImageOverlayViewer',
toggle: true, // Toggle the tool on/off upon click
},
},
evaluate: 'evaluate.cornerstoneTool.toggle',
}),
createButton({
id: 'StackScroll',
icon: 'tool-stack-scroll',
label: 'Stack Scroll',
tooltip: 'Stack Scroll',
commands: _createSetToolActiveCommands('StackScroll'),
commands: setToolActiveToolbar,
evaluate: 'evaluate.cornerstoneTool',
}),
ToolbarService.createButton({
id: 'Magnify',
icon: 'tool-magnify',
label: 'Magnify',
tooltip: 'Magnify',
commands: _createSetToolActiveCommands('Magnify'),
evaluate: 'evaluate.cornerstoneTool',
}),
ToolbarService.createButton({
id: 'Invert',
createButton({
id: 'invert',
icon: 'tool-invert',
label: 'Invert',
tooltip: 'Invert Colors',
commands: [
{
commandName: 'invertViewport',
context: 'CORNERSTONE',
},
],
evaluate: 'evaluate.action',
commands: 'invertViewport',
evaluate: 'evaluate.viewportProperties.toggle',
}),
ToolbarService.createButton({
createButton({
id: 'Probe',
icon: 'tool-probe',
label: 'Probe',
tooltip: 'Probe',
commands: setToolActiveToolbar,
evaluate: 'evaluate.cornerstoneTool',
}),
createButton({
id: 'Cine',
icon: 'tool-cine',
label: 'Cine',
tooltip: 'Cine',
commands: [
{
commandName: 'toggleCine',
context: 'CORNERSTONE',
},
],
evaluate: 'evaluate.action',
commands: 'toggleCine',
evaluate: ['evaluate.cine', 'evaluate.not3D'],
}),
ToolbarService.createButton({
id: 'DicomTagBrowser',
createButton({
id: 'Angle',
icon: 'tool-angle',
label: 'Angle',
tooltip: 'Angle',
commands: setToolActiveToolbar,
evaluate: 'evaluate.cornerstoneTool',
}),
createButton({
id: 'Magnify',
icon: 'tool-magnify',
label: 'Magnify',
tooltip: 'Magnify',
commands: setToolActiveToolbar,
evaluate: 'evaluate.cornerstoneTool',
}),
createButton({
id: 'RectangleROI',
icon: 'tool-rectangle',
label: 'Rectangle',
tooltip: 'Rectangle',
commands: setToolActiveToolbar,
evaluate: 'evaluate.cornerstoneTool',
}),
createButton({
id: 'CalibrationLine',
icon: 'tool-calibration',
label: 'Calibration',
tooltip: 'Calibration Line',
commands: setToolActiveToolbar,
evaluate: 'evaluate.cornerstoneTool',
}),
createButton({
id: 'TagBrowser',
icon: 'list-bullets',
label: 'Dicom Tag Browser',
tooltip: 'Dicom Tag Browser',
commands: [
{
commandName: 'openDICOMTagViewer',
context: 'DEFAULT',
},
],
evaluate: 'evaluate.action',
commands: 'openDICOMTagViewer',
}),
],
},

View File

@ -100,22 +100,6 @@ const toolbarButtons = [
evaluate: 'evaluate.cornerstoneTool',
},
},
{
id: 'MPR',
uiType: 'ohif.radioGroup',
props: {
icon: 'icon-mpr',
label: 'MPR',
commands: [
{
commandName: 'toggleHangingProtocol',
commandOptions: { protocolId: 'mpr' },
context: 'DEFAULT',
},
],
evaluate: 'evaluate.mpr',
},
},
// Window Level + Presets
{
id: 'WindowLevel',

View File

@ -12,7 +12,6 @@ export function initCornerstoneToolsAliases() {
cy.get('[data-cy="MoreTools-split-button-secondary"]').as('moreBtnSecondary');
cy.get('[data-cy="Layout"]').as('layoutBtn');
cy.get('.cornerstone-viewport-element').as('viewport');
cy.get('[data-cy="MPR"]').as('mprBtn');
}
//Creating aliases for Common page elements

View File

@ -56,7 +56,6 @@ function modeFactory({ modeConfiguration }) {
'Pan',
'Capture',
'Layout',
'MPR',
'Crosshairs',
'MoreTools',
]);

View File

@ -420,6 +420,34 @@ export default class ToolbarService extends PubSubService {
return;
}
if (Array.isArray(evaluate)) {
const evaluators = evaluate.map(evaluatorName => {
const evaluateFunction = this._evaluateFunction[evaluatorName];
if (!evaluateFunction) {
throw new Error(
`Evaluate function not found for name: ${evaluatorName}, you can register an evaluate function with the getToolbarModule in your extensions`
);
}
return evaluateFunction;
});
props.evaluate = args => {
const results = evaluators.map(evaluator => evaluator(args));
const mergedResult = results.reduce((acc, result) => {
return {
...acc,
...result,
};
}, {});
return mergedResult;
};
return;
}
if (typeof evaluate === 'string') {
const evaluateFunction = this._evaluateFunction[evaluate];
@ -429,7 +457,7 @@ export default class ToolbarService extends PubSubService {
}
throw new Error(
`Evaluate function not found for name: ${evaluate}, you can register an evaluate function with the getToolbarModule in your extensions`
`Evaluate function not found for name: ${evaluate}, you can register an evaluate function with the getToolbarModule in your extensions`
);
}
@ -443,7 +471,7 @@ export default class ToolbarService extends PubSubService {
}
throw new Error(
`Evaluate function not found for name: ${name}, you can register an evaluate function with the getToolbarModule in your extensions`
`Evaluate function not found for name: ${name}, you can register an evaluate function with the getToolbarModule in your extensions`
);
}
};

View File

@ -1,6 +1,6 @@
import type { RunCommand } from '../../types/Command';
export type EvaluatePublic = string | EvaluateFunction;
export type EvaluatePublic = string | EvaluateFunction | string[];
export type EvaluateFunction = (props: Record<string, unknown>) => {
disabled: boolean;

View File

@ -304,6 +304,16 @@ export type Protocol = {
*/
minSeriesLoaded: number;
};
/*
* The icon to use for this protocol. This is used to display the protocol
* in the advanced layout selector.
*/
icon?: string;
/** Indicates if the protocol is a preset or not. Useful for setting presets for the layout selector */
isPreset?: true;
};
/** Used to dynamically generate protocols.

View File

@ -657,7 +657,6 @@ onModeEnter: ({ servicesManager, extensionManager, commandsManager }) => {
'Pan',
'Capture',
'Layout',
'MPR',
'Crosshairs',
'MoreTools',
]);

View File

@ -184,7 +184,6 @@ function modeFactory({ modeConfiguration }) {
'Pan',
'Capture',
'Layout',
'MPR',
'Crosshairs',
'MoreTools',
]);

View File

@ -243,7 +243,6 @@ toolbarService.createButtonSection('primary', [
'Pan',
'Capture',
'Layout',
'MPR',
'Crosshairs',
'MoreTools',
]);

View File

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="29px" height="18px" viewBox="0 0 29 18" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>layout-advanced-3d-four-up</title>
<g id="Layout-select----FINAL" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="layout-advanced-3d-four-up" transform="translate(1, 1)" stroke="currentColor">
<rect id="Rectangle" x="0" y="0" width="27" height="16.2" rx="2"></rect>
<path d="M0,0 L11.8857143,-2.22044605e-16 C12.9902838,5.56104062e-16 13.8857143,0.8954305 13.8857143,2 L13.8857143,14.2 C13.8857143,15.3045695 12.9902838,16.2 11.8857143,16.2 L0,16.2 L0,16.2 L0,0 Z" id="Rectangle" transform="translate(6.9429, 8.1) scale(-1, 1) translate(-6.9429, -8.1)"></path>
<path d="M13.8857143,0 L25,0 C26.1045695,-1.18396092e-15 27,0.8954305 27,2 L27,8.5 L27,8.5 L13.8857143,8.5 L13.8857143,0 Z" id="Rectangle" fill="#263A71"></path>
<path d="M0,8.48571429 L13.8857143,8.48571429 L13.8857143,14.2 C13.8857143,15.3045695 12.9902838,16.2 11.8857143,16.2 L0,16.2 L0,16.2 L0,8.48571429 Z" id="Rectangle" transform="translate(6.9429, 12.3429) scale(-1, 1) translate(-6.9429, -12.3429)"></path>
<path d="M13.8857143,8.48571429 L27,8.48571429 L27,14.2 C27,15.3045695 26.1045695,16.2 25,16.2 L13.8857143,16.2 L13.8857143,16.2 L13.8857143,8.48571429 Z" id="Rectangle"></path>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="29px" height="18px" viewBox="0 0 29 18" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>layout-advanced-3d-main</title>
<g id="Layout-select----FINAL" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="layout-advanced-3d-main" transform="translate(1, 1)" stroke="currentColor">
<rect id="Rectangle" x="0" y="0" width="27" height="16.2" rx="2"></rect>
<path d="M18,7.71428571 L27,7.71428571 L27,14.2 C27,15.3045695 26.1045695,16.2 25,16.2 L18,16.2 L18,16.2 L18,7.71428571 Z" id="Rectangle"></path>
<path d="M0,7.71428571 L9.25714286,7.71428571 L9.25714286,16.2 L2,16.2 C0.8954305,16.2 9.55813795e-16,15.3045695 8.8817842e-16,14.2 L0,7.71428571 L0,7.71428571 Z" id="Rectangle"></path>
<path d="M2,0 L25,0 C26.1045695,-2.02906125e-16 27,0.8954305 27,2 L27,8.48571429 L27,8.48571429 L0,8.48571429 L0,2 C-1.3527075e-16,0.8954305 0.8954305,1.3527075e-16 2,0 Z" id="Rectangle" fill="#263A71" transform="translate(13.5, 4.2429) scale(-1, 1) translate(-13.5, -4.2429)"></path>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="29px" height="18px" viewBox="0 0 29 18" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>layout-advanced-3d-only</title>
<g id="Layout-select----FINAL" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="layout-advanced-3d-only" transform="translate(1, 1)" fill="#263A71" stroke="currentColor">
<rect id="Rectangle" x="0" y="0" width="27" height="16.2" rx="2"></rect>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 545 B

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="29px" height="18px" viewBox="0 0 29 18" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>layout-advanced-3d-primary</title>
<g id="Layout-select----FINAL" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="layout-advanced-3d-primary" transform="translate(1, 1)" stroke="currentColor">
<rect id="Rectangle" x="0" y="0" width="27" height="16.2" rx="2"></rect>
<path d="M15.4285714,10.5 L27,10.5 L27,14 C27,15.1045695 26.1045695,16 25,16 L15.4285714,16 L15.4285714,16 L15.4285714,10.5 Z" id="Rectangle"></path>
<path d="M15.4285714,0 L25,0 C26.1045695,-2.02906125e-16 27,0.8954305 27,2 L27,5.4 L27,5.4 L15.4285714,5.4 L15.4285714,0 Z" id="Rectangle"></path>
<path d="M0,0 L14.2,0 C15.3045695,-2.02906125e-16 16.2,0.8954305 16.2,2 L16.2,14.2 C16.2,15.3045695 15.3045695,16.2 14.2,16.2 L0,16.2 L0,16.2 L0,0 Z" id="Rectangle" fill="#263A71" transform="translate(8.1, 8.1) scale(-1, 1) translate(-8.1, -8.1)"></path>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="29px" height="18px" viewBox="0 0 29 18" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>layout-advanced-axial-primary</title>
<g id="Layout-select----FINAL" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="layout-advanced-axial-primary" transform="translate(1, 1)" stroke="currentColor">
<path d="M15.4285714,0 L25,0 C26.1045695,-2.02906125e-16 27,0.8954305 27,2 L27,14.2 C27,15.3045695 26.1045695,16.2 25,16.2 L15.4285714,16.2 L15.4285714,16.2 L15.4285714,0 Z" id="Rectangle"></path>
<path d="M15.4285714,0 L25,0 C26.1045695,-2.02906125e-16 27,0.8954305 27,2 L27,8.48571429 L27,8.48571429 L15.4285714,8.48571429 L15.4285714,0 Z" id="Rectangle"></path>
<path d="M4.54747351e-13,0 L13.7,0 C14.8045695,-2.02906125e-16 15.7,0.8954305 15.7,2 L15.7,14.2 C15.7,15.3045695 14.8045695,16.2 13.7,16.2 L4.54747351e-13,16.2 L4.54747351e-13,16.2 L4.54747351e-13,0 Z" id="Rectangle" transform="translate(7.85, 8.1) scale(-1, 1) translate(-7.85, -8.1)"></path>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="29px" height="18px" viewBox="0 0 29 18" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>layout-advanced-mpr</title>
<g id="Layout-select----FINAL" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="layout-advanced-mpr" transform="translate(1, 1)" stroke="currentColor">
<path d="M17.7428571,0 L25,0 C26.1045695,-2.02906125e-16 27,0.8954305 27,2 L27,14.2 C27,15.3045695 26.1045695,16.2 25,16.2 L17.7428571,16.2 L17.7428571,16.2 L17.7428571,0 Z" id="Rectangle"></path>
<rect id="Rectangle" x="9" y="0" width="9" height="16.2"></rect>
<path d="M0,0 L7.25714286,0 C8.36171236,-1.18396092e-15 9.25714286,0.8954305 9.25714286,2 L9.25714286,14.2 C9.25714286,15.3045695 8.36171236,16.2 7.25714286,16.2 L0,16.2 L0,16.2 L0,0 Z" id="Rectangle" transform="translate(4.6286, 8.1) scale(-1, 1) translate(-4.6286, -8.1)"></path>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1017 B

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="28px" height="18px" viewBox="0 0 28 18" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>layout-common-1x1</title>
<g id="Layout-select----FINAL" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="layout-common-1x1" transform="translate(1, 1)" stroke="currentColor">
<rect id="Rectangle" x="0" y="0" width="26" height="15.6" rx="2"></rect>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 518 B

View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="28px" height="18px" viewBox="0 0 28 18" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>layout-common-1x2</title>
<g id="Layout-select----FINAL" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="layout-common-1x2" transform="translate(1, 1)" stroke="currentColor">
<rect id="Rectangle" x="0" y="0" width="26" height="15.6" rx="2"></rect>
<path d="M0,0 L11,-4.4408921e-16 C12.1045695,-6.46995335e-16 13,0.8954305 13,2 L13,13.6 C13,14.7045695 12.1045695,15.6 11,15.6 L0,15.6 L0,15.6 L0,0 Z" id="Rectangle" transform="translate(6.5, 7.8) scale(-1, 1) translate(-6.5, -7.8)"></path>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 771 B

View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="28px" height="18px" viewBox="0 0 28 18" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>layout-common-2x2</title>
<g id="Layout-select----FINAL" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="layout-common-2x2" transform="translate(1, 1)" stroke="currentColor">
<rect id="Rectangle" x="0" y="0" width="26" height="15.6" rx="2"></rect>
<path d="M0,0 L11,-4.4408921e-16 C12.1045695,-6.46995335e-16 13,0.8954305 13,2 L13,13.6 C13,14.7045695 12.1045695,15.6 11,15.6 L0,15.6 L0,15.6 L0,0 Z" id="Rectangle" transform="translate(6.5, 7.8) scale(-1, 1) translate(-6.5, -7.8)"></path>
<path d="M2,0 L24,0 C25.1045695,-2.02906125e-16 26,0.8954305 26,2 L26,8 L26,8 L0,8 L0,2 C-1.3527075e-16,0.8954305 0.8954305,1.3527075e-16 2,0 Z" id="Rectangle" transform="translate(13, 4) scale(-1, 1) translate(-13, -4)"></path>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1012 B

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="28px" height="18px" viewBox="0 0 28 18" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>layout-common-2x3</title>
<g id="Layout-select----FINAL" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="layout-common-2x3" transform="translate(1, 1)" stroke="currentColor">
<rect id="Rectangle" x="0" y="0" width="26" height="15.6" rx="2"></rect>
<path d="M0,0 L7,0 C8.1045695,-2.02906125e-16 9,0.8954305 9,2 L9,13.6 C9,14.7045695 8.1045695,15.6 7,15.6 L0,15.6 L0,15.6 L0,0 Z" id="Rectangle" transform="translate(4.5, 7.8) scale(-1, 1) translate(-4.5, -7.8)"></path>
<path d="M0,0 L15,-4.4408921e-16 C16.1045695,-6.46995335e-16 17,0.8954305 17,2 L17,13.6 C17,14.7045695 16.1045695,15.6 15,15.6 L0,15.6 L0,15.6 L0,0 Z" id="Rectangle" transform="translate(8.5, 7.8) scale(-1, 1) translate(-8.5, -7.8)"></path>
<path d="M2,0 L24,0 C25.1045695,-2.02906125e-16 26,0.8954305 26,2 L26,8 L26,8 L0,8 L0,2 C-1.3527075e-16,0.8954305 0.8954305,1.3527075e-16 2,0 Z" id="Rectangle" transform="translate(13, 4) scale(-1, 1) translate(-13, -4)"></path>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="28px" height="28px" viewBox="0 0 28 28" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>tool-3d-rotate</title>
<g id="tool-3d-rotate" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<rect id="Rectangle" x="0" y="0" width="28" height="28"></rect>
<g id="Group" transform="translate(4, 3)" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5">
<g id="group">
<polyline id="Path" points="6 21.996 8.5 19.998 6 17.996"></polyline>
<path d="M8.5,19.998 C5.038,20.652 1,18.498 0,14.998" id="Path"></path>
<polyline id="Path" points="14 0 11.5 1.998 14 4"></polyline>
<path d="M11.5,1.998 C14.962,1.344 19,3.498 20,6.998" id="Path"></path>
</g>
<g id="3d" transform="translate(5, 5.75)">
<polygon id="Path" points="5 0 0 2.1875 5 4.375 10 2.1875"></polygon>
<polyline id="Path" points="0 2.1875 0 8.125 5 10.3125 10 8.125 10 2.1875"></polyline>
<line x1="5" y1="4.375" x2="5" y2="10.3125" id="Path"></line>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -1,6 +1,21 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 21 21">
<g fill="none" fill-rule="evenodd" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5">
<path fill="currentColor" d="M8.313 7.52c0 .438-.355.793-.792.793H1.188c-.438 0-.792-.355-.792-.792V1.188c0-.438.354-.792.791-.792h6.334c.437 0 .792.354.792.791v6.334z" transform="translate(1 1)"/>
<path d="M18.604 7.52c0 .438-.354.793-.791.793h-6.334c-.437 0-.791-.355-.791-.792V1.188c0-.438.354-.792.791-.792h6.334c.437 0 .791.354.791.791v6.334zM8.313 17.813c0 .437-.355.791-.792.791H1.188c-.438 0-.792-.354-.792-.791v-6.334c0-.437.354-.791.791-.791h6.334c.437 0 .792.354.792.791v6.334zM18.604 17.813c0 .437-.354.791-.791.791h-6.334c-.437 0-.791-.354-.791-.791v-6.334c0-.437.354-.791.791-.791h6.334c.437 0 .791.354.791.791v6.334z" transform="translate(1 1)"/>
<?xml version="1.0" encoding="UTF-8"?>
<svg width="28px" height="28px" viewBox="0 0 28 28" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>tool-layout</title>
<g id="tool-layout" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<rect id="Rectangle" x="0" y="0" width="28" height="28"></rect>
<g id="Group" transform="translate(0.5, 0.5)" stroke="currentColor">
<path d="M5.45696821e-12,9.49450549 L5.45696821e-12,17.3947362 C5.45710348e-12,18.4993057 0.8954305,19.3947362 2,19.3947362 L10.861569,19.3947362 L10.861569,19.3947362" id="Path-3" stroke-width="1.25" stroke-linecap="round"></path>
<path d="M9.49450549,0 L9.49450549,3.85708618 C9.49450549,4.96165568 10.389936,5.85708618 11.4945055,5.85708618 L19.195248,5.85708618 L19.195248,5.85708618" id="Path-3" stroke-width="1.25" stroke-linecap="round" transform="translate(14.3449, 2.9285) rotate(180) translate(-14.3449, -2.9285)"></path>
<path d="M0.0997440877,-0.0997440877 L0.0997440877,7.80048659 C0.0997440877,8.90505609 0.995174588,9.80048659 2.09974409,9.80048659 L9.80048659,9.80048659 L9.80048659,9.80048659" id="Path-3" stroke-width="1.25" stroke-linecap="round" transform="translate(4.9501, 4.8504) rotate(90) translate(-4.9501, -4.8504)"></path>
<g id="Group-10" opacity="0.550000012" transform="translate(2, 2)" stroke-linecap="round" stroke-width="1.5">
<line x1="7.5" y1="14.5" x2="7.5" y2="0.5" id="Line-2"></line>
<line x1="7" y1="14" x2="7" y2="1" id="Line-2" transform="translate(7, 7.5) rotate(90) translate(-7, -7.5)"></line>
</g>
<g id="gear" transform="translate(20.6492, 15.8718) rotate(-20) translate(-20.6492, -15.8718)translate(15.525, 10.1048)" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.25">
<circle id="Oval" cx="5.12378058" cy="5.76719178" r="1.28158482"></circle>
<path d="M6.21361819,0.80762797 L6.59086967,2.04907966 C6.72061095,2.47639719 7.15892721,2.73042581 7.59419808,2.63056421 L8.85259487,2.33893009 C9.34216301,2.22858155 9.84650092,2.45011894 10.0964357,2.88530436 C10.3463705,3.32048979 10.2835655,3.86774756 9.94154064,4.23499782 L9.06128718,5.18481538 C8.75980718,5.51248709 8.75980718,6.0165454 9.06128718,6.3442171 L9.94154064,7.29403466 C10.2835655,7.66128493 10.3463705,8.2085427 10.0964357,8.64372812 C9.84650092,9.07891354 9.34216301,9.30045094 8.85259487,9.1901024 L7.59419808,8.89846827 C7.15663131,8.79632613 6.71524652,9.05279063 6.58730228,9.48352022 L6.2100508,10.7249719 C6.0649735,11.2053044 5.62242271,11.5339347 5.12065911,11.5339347 C4.61889551,11.5339347 4.17634472,11.2053044 4.03126742,10.7249719 L3.65669148,9.48352022 C3.52807698,9.05567912 3.08963152,8.80095947 2.65425492,8.90114382 L1.39585813,9.19277794 C0.90628999,9.30312648 0.401952083,9.08158909 0.152017275,8.64640366 C-0.0979175321,8.21121824 -0.035112502,7.66396047 0.306912358,7.29671021 L1.18716582,6.34689264 C1.48864582,6.01922094 1.48864582,5.51516263 1.18716582,5.18749093 L0.306912358,4.23767336 C-0.035112502,3.8704231 -0.0979175321,3.32316533 0.152017275,2.88797991 C0.401952083,2.45279448 0.90628999,2.23125709 1.39585813,2.34160563 L2.65425492,2.63323975 C3.08937078,2.73350334 3.52770594,2.47923776 3.65669148,2.0517552 L4.03483481,0.810303513 C4.17932126,0.32979188 4.62146882,0.000617032116 5.12323309,0 C5.62499736,-0.000615299293 6.06795204,0.327472644 6.21361819,0.80762797 Z" id="Path"></path>
</g>
<polygon id="w/l-chevron" stroke-linejoin="round" transform="translate(20.5, 25.25) rotate(-360) translate(-20.5, -25.25)" points="20.5 26.75 18 24.1184211 18.35 23.75 20.5 26.0131579 22.65 23.75 23 24.1184211"></polygon>
</g>
</g>
</svg>
</svg>

Before

Width:  |  Height:  |  Size: 883 B

After

Width:  |  Height:  |  Size: 3.7 KiB

View File

@ -145,6 +145,20 @@ import oldTrash from './../../assets/icons/old-trash.svg';
import oldPlay from './../../assets/icons/old-play.svg';
import oldStop from './../../assets/icons/old-stop.svg';
/** LAYOUT */
import layoutAdvanced3DFourUp from './../../assets/icons/layout-advanced-3d-four-up.svg';
import layoutAdvanced3DMain from './../../assets/icons/layout-advanced-3d-main.svg';
import layoutAdvanced3DOnly from './../../assets/icons/layout-advanced-3d-only.svg';
import layoutAdvanced3DPrimary from './../../assets/icons/layout-advanced-3d-primary.svg';
import layoutAdvancedAxialPrimary from './../../assets/icons/layout-advanced-axial-primary.svg';
import layoutAdvancedMPR from './../../assets/icons/layout-advanced-mpr.svg';
import layoutCommon1x1 from './../../assets/icons/layout-common-1x1.svg';
import layoutCommon1x2 from './../../assets/icons/layout-common-1x2.svg';
import layoutCommon2x2 from './../../assets/icons/layout-common-2x2.svg';
import layoutCommon2x3 from './../../assets/icons/layout-common-2x3.svg';
import iconToolRotate from './../../assets/icons/tool-3d-rotate.svg';
/** New investigational use */
import investigationalUse from './../../assets/icons/illustration-investigational-use.svg';
@ -256,6 +270,7 @@ const ICONS = {
'tool-freehand-line': toolFreehand,
'tool-freehand-polygon': toolFreehandPolygon,
'tool-polygon': toolPolygon,
'tool-3d-rotate': iconToolRotate,
'edit-patient': editPatient,
'icon-mpr': iconMPR,
'icon-next-inactive': iconNextInactive,
@ -295,6 +310,18 @@ const ICONS = {
'old-play': oldPlay,
'old-stop': oldStop,
/** LAYOUT */
'layout-advanced-3d-four-up': layoutAdvanced3DFourUp,
'layout-advanced-3d-main': layoutAdvanced3DMain,
'layout-advanced-3d-only': layoutAdvanced3DOnly,
'layout-advanced-3d-primary': layoutAdvanced3DPrimary,
'layout-advanced-axial-primary': layoutAdvancedAxialPrimary,
'layout-advanced-mpr': layoutAdvancedMPR,
'layout-common-1x1': layoutCommon1x1,
'layout-common-1x2': layoutCommon1x2,
'layout-common-2x2': layoutCommon2x2,
'layout-common-2x3': layoutCommon2x3,
/** New investigational use */
'illustration-investigational-use': investigationalUse,
};

View File

@ -0,0 +1,34 @@
import React from 'react';
import PropTypes from 'prop-types';
import Icon from '../Icon/Icon';
function LayoutPreset({ onSelection, title, icon, commandOptions, classNames }) {
return (
<div
className={classNames}
onClick={() => {
onSelection(commandOptions);
}}
>
<Icon
name={icon}
className="group-hover:text-primary-light"
/>
{title && <div className="font-inter text-sm text-white">{title}</div>}
</div>
);
}
LayoutPreset.defaultProps = {
onSelection: () => {},
};
LayoutPreset.propTypes = {
onSelection: PropTypes.func.isRequired,
title: PropTypes.string,
icon: PropTypes.string.isRequired,
commandOptions: PropTypes.object.isRequired,
classNames: PropTypes.string,
};
export default LayoutPreset;

View File

@ -0,0 +1,2 @@
import LayoutPreset from './LayoutPreset';
export default LayoutPreset;

View File

@ -21,7 +21,6 @@ function LayoutSelector({ onSelection, rows, columns }) {
gridTemplateRows: gridSize.repeat(rows),
backgroundColor: '#090c29', // primary-dark
}}
className="p-2"
>
{Array.apply(null, Array(rows * columns))
.map(function (_, i) {
@ -30,12 +29,8 @@ function LayoutSelector({ onSelection, rows, columns }) {
.map(index => (
<div
key={index}
style={{
border: '1px solid white',
backgroundColor: isHovered(index) ? '#5acce6' : '#0b1a42',
}}
className={`border-primary-dark border ${isHovered(index) ? 'bg-primary-active' : 'bg-[#04225b]'} cursor-pointer`}
data-cy={`Layout-${index % columns}-${Math.floor(index / columns)}`}
className="cursor-pointer"
onClick={() => {
const x = index % columns;
const y = Math.floor(index / columns);
@ -55,7 +50,7 @@ function LayoutSelector({ onSelection, rows, columns }) {
LayoutSelector.defaultProps = {
onSelection: () => {},
columns: 3,
columns: 4,
rows: 3,
};

View File

@ -16,6 +16,7 @@ const ToolbarButton = ({
className,
size,
toolTipClassName,
disableToolTip = false,
...rest
//
}) => {
@ -37,6 +38,7 @@ const ToolbarButton = ({
content={shouldShowDropdown ? dropdownContent : label}
tight={shouldShowDropdown}
className={toolTipClassNameToUse}
isDisabled={disableToolTip}
>
<IconButton
size={sizeToUse}
@ -75,6 +77,7 @@ ToolbarButton.propTypes = {
dropdownContent: PropTypes.oneOfType([PropTypes.node, PropTypes.func]),
size: PropTypes.string,
toolTipClassName: PropTypes.string,
disableToolTip: PropTypes.bool,
};
export default ToolbarButton;

View File

@ -84,6 +84,7 @@ import { ToolSettings } from './AdvancedToolbox';
import { Toolbox } from './Toolbox';
import InvestigationalUseDialog from './InvestigationalUseDialog';
import MeasurementItem from './MeasurementTable/MeasurementItem';
import LayoutPreset from './LayoutPreset';
export {
AboutModal,
@ -168,6 +169,7 @@ export {
ViewportPane,
ViewportOverlay,
WindowLevelMenuItem,
LayoutPreset,
LegacySplitButton,
ToolSettings,
Toolbox,

View File

@ -118,6 +118,7 @@ export {
Toolbox,
InvestigationalUseDialog,
MeasurementItem,
LayoutPreset,
} from './components';
export { useSessionStorage } from './hooks';

View File

@ -30,5 +30,5 @@ body {
pointer-events: none;
cursor: not-allowed;
user-select: none;
opacity: 0.35;
opacity: 0.4;
}

View File

@ -2403,7 +2403,7 @@
resolved "https://registry.yarnpkg.com/@emotion/unitless/-/unitless-0.8.1.tgz#182b5a4704ef8ad91bde93f7a860a88fd92c79a3"
integrity sha512-KOEGMu6dmJZtpadb476IsZBclKvILjopjUii3V+7MnXIQCYh8W3NgNcgwo21n9LXZX6EDIKvqfjYxXebDwxKmQ==
"@emotion/use-insertion-effect-with-fallbacks@^1.0.1":
"@emotion/use-insertion-effect-with-fallbacks@^1.0.0", "@emotion/use-insertion-effect-with-fallbacks@^1.0.1":
version "1.0.1"
resolved "https://registry.yarnpkg.com/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.0.1.tgz#08de79f54eb3406f9daaf77c76e35313da963963"
integrity sha512-jT/qyKZ9rzLErtrjGgdkMBn2OP8wl0G3sQlBb3YPryvKHsjvINUhVaPFfP+fpBcOkmrVOVEEHQFJ7nbj2TH2gw==