fix(bugs): enhancements and bugs in several areas (#4086)

This commit is contained in:
Ibrahim 2024-05-06 14:55:32 -04:00 committed by GitHub
parent 01fe7fab3d
commit 730f434910
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
19 changed files with 146 additions and 43 deletions

View File

@ -15,23 +15,28 @@ function promptHydrateRT({
hydrateRTDisplaySet,
}) {
const { uiViewportDialogService } = servicesManager.services;
const extensionManager = servicesManager._extensionManager;
const appConfig = extensionManager._appConfig;
return new Promise(async function (resolve, reject) {
const promptResult = await _askHydrate(uiViewportDialogService, viewportId);
const promptResult = appConfig?.disableConfirmationPrompts
? RESPONSE.HYDRATE_SEG
: await _askHydrate(uiViewportDialogService, viewportId);
if (promptResult === RESPONSE.HYDRATE_SEG) {
preHydrateCallbacks?.forEach(callback => {
callback();
});
const isHydrated = await hydrateRTDisplaySet({
rtDisplaySet,
viewportId,
toolGroupId,
servicesManager,
});
window.setTimeout(async () => {
const isHydrated = await hydrateRTDisplaySet({
rtDisplaySet,
viewportId,
toolGroupId,
servicesManager,
});
resolve(isHydrated);
resolve(isHydrated);
}, 0);
}
});
}

View File

@ -258,9 +258,12 @@ const commandsModule = ({
labelmapObj.metadata = [];
const segmentationInOHIF = segmentationService.getSegmentation(segmentationId);
labelmapObj.segmentsOnLabelmap.forEach(segmentIndex => {
segmentationInOHIF.segments.forEach(segment => {
// segmentation service already has a color for each segment
const segment = segmentationInOHIF?.segments[segmentIndex];
if (!segment) {
return;
}
const segmentIndex = segment.segmentIndex;
const { label, color } = segment;
const RecommendedDisplayCIELabValue = dcmjs.data.Colors.rgb2DICOMLAB(

View File

@ -14,21 +14,27 @@ function promptHydrateSEG({
hydrateSEGDisplaySet,
}) {
const { uiViewportDialogService } = servicesManager.services;
const extensionManager = servicesManager._extensionManager;
const appConfig = extensionManager._appConfig;
return new Promise(async function (resolve, reject) {
const promptResult = await _askHydrate(uiViewportDialogService, viewportId);
const promptResult = appConfig?.disableConfirmationPrompts
? RESPONSE.HYDRATE_SEG
: await _askHydrate(uiViewportDialogService, viewportId);
if (promptResult === RESPONSE.HYDRATE_SEG) {
preHydrateCallbacks?.forEach(callback => {
callback();
});
const isHydrated = await hydrateSEGDisplaySet({
segDisplaySet,
viewportId,
});
window.setTimeout(async () => {
const isHydrated = await hydrateSEGDisplaySet({
segDisplaySet,
viewportId,
});
resolve(isHydrated);
resolve(isHydrated);
}, 0);
}
});
}

View File

@ -137,20 +137,8 @@ function CustomizableViewportOverlay({
return;
}
const imageData = viewport.getImageData();
const scale = viewport.getZoom();
if (!imageData) {
return;
}
if (camera.scale) {
setScale(camera.scale);
return;
}
const { spacing } = imageData;
// convert parallel scale to scale
const scale = (element.clientHeight * spacing[0] * 0.5) / camera.parallelScale;
setScale(scale);
}
};

View File

@ -568,7 +568,7 @@ class SegmentationService extends PubSubService {
rgba,
} = segmentInfo;
const { x, y, z } = segDisplaySet.centroids.get(segmentIndex);
const { x, y, z } = segDisplaySet.centroids.get(segmentIndex) || { x: 0, y: 0, z: 0 };
const centerWorld = derivedVolume.imageData.indexToWorld([x, y, z]);
segmentation.cachedStats = {

View File

@ -39,6 +39,7 @@ async function createReportAsync({ servicesManager, getReport, reportType = 'mea
message: error.message || `Failed to store ${reportType}`,
type: 'error',
});
throw new Error(`Failed to store ${reportType}. Error: ${error.message || 'Unknown error'}`);
} finally {
uiDialogService.dismiss({ id: loadingDialogId });
}

View File

@ -99,7 +99,9 @@ const extension: Types.Extensions.Extension = {
const { microscopyService } = servicesManager.services;
const activeInteractions = microscopyService.getActiveInteractions();
if (!activeInteractions) {
return false;
}
const isPrimaryActive = activeInteractions.find(interactions => {
const sameMouseButton = interactions[1].bindings.mouseButtons.includes('left');

View File

@ -11,12 +11,15 @@ const RESPONSE = {
function promptBeginTracking({ servicesManager, extensionManager }, ctx, evt) {
const { uiViewportDialogService } = servicesManager.services;
const appConfig = extensionManager._appConfig;
// When the state change happens after a promise, the state machine sends the retult in evt.data;
// In case of direct transition to the state, the state machine sends the data in evt;
const { viewportId, StudyInstanceUID, SeriesInstanceUID } = evt.data || evt;
return new Promise(async function (resolve, reject) {
let promptResult = await _askTrackMeasurements(uiViewportDialogService, viewportId);
let promptResult = appConfig?.disableConfirmationPrompts
? RESPONSE.SET_STUDY_AND_SERIES
: await _askTrackMeasurements(uiViewportDialogService, viewportId);
resolve({
userResponse: promptResult,

View File

@ -15,9 +15,10 @@ function promptHydrateStructuredReport({ servicesManager, extensionManager, appC
const { uiViewportDialogService, displaySetService } = servicesManager.services;
const { viewportId, displaySetInstanceUID } = evt;
const srDisplaySet = displaySetService.getDisplaySetByUID(displaySetInstanceUID);
return new Promise(async function (resolve, reject) {
const promptResult = await _askTrackMeasurements(uiViewportDialogService, viewportId);
const promptResult = appConfig?.disableConfirmationPrompts
? RESPONSE.HYDRATE_REPORT
: await _askTrackMeasurements(uiViewportDialogService, viewportId);
// Need to do action here... So we can set state...
let StudyInstanceUID, SeriesInstanceUIDs;

View File

@ -2,7 +2,7 @@ import { createReportAsync, createReportDialogPrompt } from '@ohif/extension-def
import getNextSRSeriesNumber from '../../_shared/getNextSRSeriesNumber';
import RESPONSE from '../../_shared/PROMPT_RESPONSES';
function promptSaveReport({ servicesManager, commandsManager, extensionManager }, ctx, evt) {
async function promptSaveReport({ servicesManager, commandsManager, extensionManager }, ctx, evt) {
const { uiDialogService, measurementService, displaySetService } = servicesManager.services;
const viewportId = evt.viewportId === undefined ? evt.data.viewportId : evt.viewportId;
const isBackupSave = evt.isBackupSave === undefined ? evt.data.isBackupSave : evt.isBackupSave;
@ -12,8 +12,7 @@ function promptSaveReport({ servicesManager, commandsManager, extensionManager }
const { trackedStudy, trackedSeries } = ctx;
let displaySetInstanceUIDs;
return new Promise(async function (resolve, reject) {
// TODO: Fallback if (uiDialogService) {
try {
const promptResult = await createReportDialogPrompt(uiDialogService, {
extensionManager,
});
@ -57,15 +56,17 @@ function promptSaveReport({ servicesManager, commandsManager, extensionManager }
// Do nothing
}
resolve({
return {
userResponse: promptResult.action,
createdDisplaySetInstanceUIDs: displaySetInstanceUIDs,
StudyInstanceUID,
SeriesInstanceUID,
viewportId,
isBackupSave,
});
});
};
} catch (error) {
return null;
}
}
export default promptSaveReport;

View File

@ -11,12 +11,15 @@ const RESPONSE = {
function promptTrackNewStudy({ servicesManager, extensionManager }, ctx, evt) {
const { UIViewportDialogService } = servicesManager.services;
const appConfig = extensionManager._appConfig;
// When the state change happens after a promise, the state machine sends the retult in evt.data;
// In case of direct transition to the state, the state machine sends the data in evt;
const { viewportId, StudyInstanceUID, SeriesInstanceUID } = evt.data || evt;
return new Promise(async function (resolve, reject) {
let promptResult = await _askTrackMeasurements(UIViewportDialogService, viewportId);
let promptResult = appConfig?.disableConfirmationPrompts
? RESPONSE.SET_STUDY_AND_SERIES
: await _askTrackMeasurements(UIViewportDialogService, viewportId);
if (promptResult === RESPONSE.SET_STUDY_AND_SERIES) {
promptResult = ctx.isDirty

View File

@ -1,3 +1,15 @@
const colours = {
'viewport-0': 'rgb(200, 0, 0)',
'viewport-1': 'rgb(200, 200, 0)',
'viewport-2': 'rgb(0, 200, 0)',
};
const colorsByOrientation = {
axial: 'rgb(200, 0, 0)',
sagittal: 'rgb(200, 200, 0)',
coronal: 'rgb(0, 200, 0)',
};
function initDefaultToolGroup(extensionManager, toolGroupService, commandsManager, toolGroupId) {
const utilityModule = extensionManager.getModuleEntry(
'@ohif/extension-cornerstone.utilityModule.tools'
@ -132,6 +144,9 @@ function initMPRToolGroup(extensionManager, toolGroupService, commandsManager) {
'@ohif/extension-cornerstone.utilityModule.tools'
);
const serviceManager = extensionManager._servicesManager;
const { cornerstoneViewportService } = serviceManager.services;
const { toolNames, Enums } = utilityModule.exports;
const tools = {
@ -188,6 +203,20 @@ function initMPRToolGroup(extensionManager, toolGroupService, commandsManager) {
enabled: false,
panSize: 10,
},
getReferenceLineColor: viewportId => {
const viewportInfo = cornerstoneViewportService.getViewportInfo(viewportId);
const viewportOptions = viewportInfo?.viewportOptions;
if (viewportOptions) {
return (
colours[viewportOptions.id] ||
colorsByOrientation[viewportOptions.orientation] ||
'#0c0'
);
} else {
console.warn('missing viewport?', viewportId);
return '#0c0';
}
},
},
},
{ toolName: toolNames.ReferenceLines },

View File

@ -135,6 +135,14 @@ const moreTools = [
commands: setToolActiveToolbar,
evaluate: 'evaluate.cornerstoneTool',
}),
createButton({
id: 'CobbAngle',
icon: 'tool-angle',
label: 'Cobb Angle',
tooltip: 'Cobb Angle',
commands: setToolActiveToolbar,
evaluate: 'evaluate.cornerstoneTool',
}),
createButton({
id: 'Magnify',
icon: 'tool-magnify',

View File

@ -1,3 +1,15 @@
const colours = {
'viewport-0': 'rgb(200, 0, 0)',
'viewport-1': 'rgb(200, 200, 0)',
'viewport-2': 'rgb(0, 200, 0)',
};
const colorsByOrientation = {
axial: 'rgb(200, 0, 0)',
sagittal: 'rgb(200, 200, 0)',
coronal: 'rgb(0, 200, 0)',
};
function initDefaultToolGroup(
extensionManager,
toolGroupService,
@ -155,6 +167,9 @@ function initMPRToolGroup(extensionManager, toolGroupService, commandsManager, m
'@ohif/extension-cornerstone.utilityModule.tools'
);
const serviceManager = extensionManager._servicesManager;
const { cornerstoneViewportService } = serviceManager.services;
const { toolNames, Enums } = utilityModule.exports;
const tools = {
@ -221,6 +236,20 @@ function initMPRToolGroup(extensionManager, toolGroupService, commandsManager, m
enabled: false,
panSize: 10,
},
getReferenceLineColor: viewportId => {
const viewportInfo = cornerstoneViewportService.getViewportInfo(viewportId);
const viewportOptions = viewportInfo?.viewportOptions;
if (viewportOptions) {
return (
colours[viewportOptions.id] ||
colorsByOrientation[viewportOptions.orientation] ||
'#0c0'
);
} else {
console.warn('missing viewport?', viewportId);
return '#0c0';
}
},
},
},
{

View File

@ -135,6 +135,14 @@ const moreTools = [
commands: setToolActiveToolbar,
evaluate: 'evaluate.cornerstoneTool',
}),
createButton({
id: 'CobbAngle',
icon: 'tool-angle',
label: 'Cobb Angle',
tooltip: 'Cobb Angle',
commands: setToolActiveToolbar,
evaluate: 'evaluate.cornerstoneTool',
}),
createButton({
id: 'Magnify',
icon: 'tool-magnify',

View File

@ -51,7 +51,7 @@ function modeFactory({ modeConfiguration }) {
const { toolbarService } = servicesManager.services;
toolbarService.addButtons(toolbarButtons);
toolbarService.createButtonSection('primary', ['MeasurementTools', 'dragPan']);
toolbarService.createButtonSection('primary', ['MeasurementTools', 'dragPan', 'TagBrowser']);
},
onModeExit: ({ servicesManager }) => {

View File

@ -145,6 +145,20 @@ const toolbarButtons = [
evaluate: 'evaluate.microscopyTool',
},
},
{
id: 'TagBrowser',
uiType: 'ohif.radioGroup',
props: {
icon: 'dicom-tag-browser',
label: 'Dicom Tag Browser',
commands: [
{
commandName: 'openDICOMTagViewer',
},
],
evaluate: 'evaluate.action',
},
},
];
export default toolbarButtons;

View File

@ -124,6 +124,7 @@ Here are a list of some options available:
- `acceptHeader` : accept header to request specific dicom transfer syntax ex : [ 'multipart/related; type=image/jls; q=1', 'multipart/related; type=application/octet-stream; q=0.1' ]
- `investigationalUseDialog`: This should contain an object with `option` value, it can be either `always` which always shows the dialog once per session, `never` which never shows the dialog, or `configure` which shows the dialog once and won't show it again until a set number of days defined by the user, if it's set to configure, you are required to add an additional property `days` which is the number of days to wait before showing the dialog again.
- `groupEnabledModesFirst`: boolean, if set to true, all valid modes for the study get grouped together first, then the rest of the modes. If false, all modes are shown in the order they are defined in the configuration.
- `disableConfirmationPrompts`: boolean, if set to true, it skips confirmation prompts for measurement tracking and hydration.
- `showPatientInfo`: string, if set to 'visible', the patient info header will be shown and its initial state is expanded. If set to 'visibleCollapsed', the patient info header will be shown but it's initial state is collapsed. If set to 'disabled', the patient info header will never be shown, and if set to 'visibleReadOnly', the patient info header will be shown and always expanded.
- `requestTransferSyntaxUID` : Request a specific Transfer syntax from dicom web server ex: 1.2.840.10008.1.2.4.80 (applied only if acceptHeader is not set)
- `omitQuotationForMultipartRequest`: Some servers (e.g., .NET) require the `multipart/related` request to be sent without quotation marks. Defaults to `false`. If your server doesn't require this, then setting this flag to `true` might improve performance (by removing the need for preflight requests). Also note that

View File

@ -94,6 +94,7 @@ const Select = ({
components={_components}
placeholder={placeholder}
options={options}
blurInputOnSelect={true}
value={value && Array.isArray(value) ? selectedOptions : value}
onChange={(selectedOptions, { action }) => {
const newSelection = !selectedOptions.length