fix: jump to measurement forever (#4867)
This commit is contained in:
1 parent
eafa2efda5
commit
d7bd36544a
39 files changed
+731
-448
No files matched your search
@@ -46,8 +46,8 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.20.13",
|
||||
"@cornerstonejs/adapters": "^3.7.16",
|
||||
"@cornerstonejs/core": "^3.7.16",
|
||||
"@cornerstonejs/adapters": "^3.7.17",
|
||||
"@cornerstonejs/core": "^3.7.17",
|
||||
"@kitware/vtk.js": "32.12.0",
|
||||
"react-color": "^2.19.3"
|
||||
}
|
||||
|
||||
@@ -46,8 +46,8 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.20.13",
|
||||
"@cornerstonejs/adapters": "^3.7.16",
|
||||
"@cornerstonejs/core": "^3.7.16",
|
||||
"@cornerstonejs/adapters": "^3.7.17",
|
||||
"@cornerstonejs/core": "^3.7.17",
|
||||
"@kitware/vtk.js": "32.12.0",
|
||||
"react-color": "^2.19.3"
|
||||
}
|
||||
|
||||
@@ -46,9 +46,9 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.20.13",
|
||||
"@cornerstonejs/adapters": "^3.7.16",
|
||||
"@cornerstonejs/core": "^3.7.16",
|
||||
"@cornerstonejs/tools": "^3.7.16",
|
||||
"@cornerstonejs/adapters": "^3.7.17",
|
||||
"@cornerstonejs/core": "^3.7.17",
|
||||
"@cornerstonejs/tools": "^3.7.17",
|
||||
"classnames": "^2.3.2"
|
||||
}
|
||||
}
|
||||
@@ -77,28 +77,4 @@ export default function init({
|
||||
SRRectangleROI: dashedLine,
|
||||
global: {},
|
||||
});
|
||||
|
||||
measurementService.subscribe(
|
||||
MeasurementService.EVENTS.JUMP_TO_MEASUREMENT_LAYOUT,
|
||||
({ viewportId, measurement, isConsumed }) => {
|
||||
if (isConsumed) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const currentViewport = cornerstoneViewportService.getCornerstoneViewport(viewportId);
|
||||
const { viewPlaneNormal } = currentViewport.getCamera();
|
||||
const referencedImageId = csToolsUtils.getClosestImageIdForStackViewport(
|
||||
currentViewport as StackViewport,
|
||||
measurement.points[0],
|
||||
viewPlaneNormal
|
||||
);
|
||||
const imageIndex = (currentViewport as StackViewport)
|
||||
.getImageIds()
|
||||
.indexOf(referencedImageId);
|
||||
csUtils.jumpToSlice(currentViewport.element, { imageIndex });
|
||||
} catch (error) {
|
||||
console.warn('Unable to jump to image based on measurement coordinate', error);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -42,8 +42,8 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.20.13",
|
||||
"@cornerstonejs/core": "^3.7.16",
|
||||
"@cornerstonejs/tools": "^3.7.16",
|
||||
"@cornerstonejs/core": "^3.7.17",
|
||||
"@cornerstonejs/tools": "^3.7.17",
|
||||
"classnames": "^2.3.2"
|
||||
}
|
||||
}
|
||||
@@ -38,7 +38,7 @@
|
||||
"@cornerstonejs/codec-libjpeg-turbo-8bit": "^1.2.2",
|
||||
"@cornerstonejs/codec-openjpeg": "^1.2.4",
|
||||
"@cornerstonejs/codec-openjph": "^2.4.5",
|
||||
"@cornerstonejs/dicom-image-loader": "^3.7.16",
|
||||
"@cornerstonejs/dicom-image-loader": "^3.7.17",
|
||||
"@ohif/core": "3.10.0-beta.140",
|
||||
"@ohif/ui": "3.10.0-beta.140",
|
||||
"dcmjs": "*",
|
||||
@@ -51,11 +51,11 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.20.13",
|
||||
"@cornerstonejs/adapters": "^3.7.16",
|
||||
"@cornerstonejs/core": "^3.7.16",
|
||||
"@cornerstonejs/labelmap-interpolation": "^3.7.16",
|
||||
"@cornerstonejs/polymorphic-segmentation": "^3.7.16",
|
||||
"@cornerstonejs/tools": "^3.7.16",
|
||||
"@cornerstonejs/adapters": "^3.7.17",
|
||||
"@cornerstonejs/core": "^3.7.17",
|
||||
"@cornerstonejs/labelmap-interpolation": "^3.7.17",
|
||||
"@cornerstonejs/polymorphic-segmentation": "^3.7.17",
|
||||
"@cornerstonejs/tools": "^3.7.17",
|
||||
"@itk-wasm/morphological-contour-interpolation": "1.1.0",
|
||||
"@kitware/vtk.js": "32.12.0",
|
||||
"html2canvas": "^1.4.1",
|
||||
|
||||
@@ -23,12 +23,6 @@ import { WITH_NAVIGATION } from '../services/ViewportService/CornerstoneViewport
|
||||
|
||||
const STACK = 'stack';
|
||||
|
||||
/**
|
||||
* Caches the jump to measurement operation, so that if display set is shown,
|
||||
* it can jump to the measurement.
|
||||
*/
|
||||
let cacheJumpToMeasurementEvent;
|
||||
|
||||
// Todo: This should be done with expose of internal API similar to react-vtkjs-viewport
|
||||
// Then we don't need to worry about the re-renders if the props change.
|
||||
const OHIFCornerstoneViewport = React.memo(
|
||||
@@ -99,6 +93,7 @@ const OHIFCornerstoneViewport = React.memo(
|
||||
cornerstoneCacheService,
|
||||
viewportActionCornersService,
|
||||
customizationService,
|
||||
measurementService,
|
||||
} = servicesManager.services;
|
||||
|
||||
const [viewportDialogState] = useViewportDialog();
|
||||
@@ -263,14 +258,6 @@ const OHIFCornerstoneViewport = React.memo(
|
||||
|
||||
const presentations = getViewportPresentations(viewportId, viewportOptions);
|
||||
|
||||
let measurement;
|
||||
if (cacheJumpToMeasurementEvent?.viewportId === viewportId) {
|
||||
measurement = cacheJumpToMeasurementEvent.measurement;
|
||||
// Delete the position presentation so that viewport navigates direct
|
||||
presentations.positionPresentation = null;
|
||||
cacheJumpToMeasurementEvent = null;
|
||||
}
|
||||
|
||||
// Note: This is a hack to get the grid to re-render the OHIFCornerstoneViewport component
|
||||
// Used for segmentation hydration right now, since the logic to decide whether
|
||||
// a viewport needs to render a segmentation lives inside the CornerstoneViewportService
|
||||
@@ -288,10 +275,6 @@ const OHIFCornerstoneViewport = React.memo(
|
||||
displaySetOptions,
|
||||
presentations
|
||||
);
|
||||
|
||||
if (measurement) {
|
||||
cs3DTools.annotation.selection.setAnnotationSelected(measurement.uid);
|
||||
}
|
||||
};
|
||||
|
||||
loadViewportData();
|
||||
@@ -312,16 +295,13 @@ const OHIFCornerstoneViewport = React.memo(
|
||||
return;
|
||||
}
|
||||
|
||||
const unsubscribeFromJumpToMeasurementEvents = _subscribeToJumpToMeasurementEvents(
|
||||
elementRef,
|
||||
viewportId,
|
||||
servicesManager
|
||||
const { unsubscribe } = measurementService.subscribe(
|
||||
MeasurementService.EVENTS.JUMP_TO_MEASUREMENT_VIEWPORT,
|
||||
event => handleJumpToMeasurement(event, elementRef, viewportId, cornerstoneViewportService)
|
||||
);
|
||||
|
||||
_checkForCachedJumpToMeasurementEvents(elementRef, viewportId, displaySets, servicesManager);
|
||||
|
||||
return () => {
|
||||
unsubscribeFromJumpToMeasurementEvents();
|
||||
unsubscribe();
|
||||
};
|
||||
}, [displaySets, elementRef, viewportId, isJumpToMeasurementDisabled, servicesManager]);
|
||||
|
||||
@@ -427,99 +407,52 @@ const OHIFCornerstoneViewport = React.memo(
|
||||
areEqual
|
||||
);
|
||||
|
||||
function _subscribeToJumpToMeasurementEvents(elementRef, viewportId, servicesManager) {
|
||||
const { measurementService, cornerstoneViewportService } = servicesManager.services;
|
||||
// Helper function to handle jumping to measurements
|
||||
function handleJumpToMeasurement(event, elementRef, viewportId, cornerstoneViewportService) {
|
||||
const { measurement, isConsumed } = event;
|
||||
if (!measurement || isConsumed) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { unsubscribe } = measurementService.subscribe(
|
||||
MeasurementService.EVENTS.JUMP_TO_MEASUREMENT_VIEWPORT,
|
||||
props => {
|
||||
cacheJumpToMeasurementEvent = props;
|
||||
const { viewportId: jumpId, measurement, isConsumed } = props;
|
||||
if (!measurement || isConsumed) {
|
||||
return;
|
||||
}
|
||||
if (cacheJumpToMeasurementEvent.cornerstoneViewport === undefined) {
|
||||
// Decide on which viewport should handle this
|
||||
cacheJumpToMeasurementEvent.cornerstoneViewport =
|
||||
cornerstoneViewportService.getViewportIdToJump(jumpId, {
|
||||
displaySetInstanceUID: measurement.displaySetInstanceUID,
|
||||
...measurement.metadata,
|
||||
referencedImageId:
|
||||
measurement.referencedImageId || measurement.metadata?.referencedImageId,
|
||||
});
|
||||
}
|
||||
if (cacheJumpToMeasurementEvent.cornerstoneViewport !== viewportId) {
|
||||
return;
|
||||
}
|
||||
_jumpToMeasurement(measurement, elementRef, viewportId, servicesManager);
|
||||
}
|
||||
const enabledElement = getEnabledElement(elementRef.current);
|
||||
|
||||
if (!enabledElement) {
|
||||
return;
|
||||
}
|
||||
|
||||
const viewport = enabledElement.viewport as csTypes.IStackViewport | csTypes.IVolumeViewport;
|
||||
|
||||
const { metadata, displaySetInstanceUID } = measurement;
|
||||
|
||||
const viewportDisplaySets = cornerstoneViewportService.getViewportDisplaySets(viewportId);
|
||||
|
||||
const showingDisplaySet = viewportDisplaySets.find(
|
||||
ds => ds.displaySetInstanceUID === displaySetInstanceUID
|
||||
);
|
||||
|
||||
return unsubscribe;
|
||||
}
|
||||
let metadataToUse = metadata;
|
||||
// if it is not showing the displaySet we need to remove the FOR from the metadata
|
||||
if (!showingDisplaySet) {
|
||||
metadataToUse = {
|
||||
...metadata,
|
||||
FrameOfReferenceUID: undefined,
|
||||
};
|
||||
}
|
||||
|
||||
// Check if there is a queued jumpToMeasurement event
|
||||
function _checkForCachedJumpToMeasurementEvents(
|
||||
elementRef,
|
||||
viewportId,
|
||||
displaySets,
|
||||
servicesManager
|
||||
) {
|
||||
if (!cacheJumpToMeasurementEvent) {
|
||||
return;
|
||||
}
|
||||
if (cacheJumpToMeasurementEvent.isConsumed) {
|
||||
cacheJumpToMeasurementEvent = null;
|
||||
return;
|
||||
}
|
||||
const displaysUIDs = displaySets.map(displaySet => displaySet.displaySetInstanceUID);
|
||||
if (!displaysUIDs?.length) {
|
||||
// Todo: make it work with cases where we want to define FOR based measurements too
|
||||
if (!viewport.isReferenceViewable(metadataToUse, WITH_NAVIGATION)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Jump to measurement if the measurement exists
|
||||
const { measurement } = cacheJumpToMeasurementEvent;
|
||||
if (measurement && elementRef) {
|
||||
if (displaysUIDs.includes(measurement?.displaySetInstanceUID)) {
|
||||
_jumpToMeasurement(measurement, elementRef, viewportId, servicesManager);
|
||||
}
|
||||
try {
|
||||
viewport.setViewReference(metadata);
|
||||
viewport.render();
|
||||
} catch (e) {
|
||||
console.warn('Unable to apply', metadata, e);
|
||||
}
|
||||
}
|
||||
|
||||
function _jumpToMeasurement(measurement, targetElementRef, viewportId, servicesManager) {
|
||||
const { viewportGridService } = servicesManager.services;
|
||||
|
||||
const targetElement = targetElementRef.current;
|
||||
|
||||
// Todo: setCornerstoneMeasurementActive should be handled by the toolGroupManager
|
||||
// to set it properly
|
||||
// setCornerstoneMeasurementActive(measurement);
|
||||
|
||||
viewportGridService.setActiveViewportId(viewportId);
|
||||
|
||||
const enabledElement = getEnabledElement(targetElement);
|
||||
|
||||
if (enabledElement) {
|
||||
// See how the jumpToSlice() of Cornerstone3D deals with imageIdx param.
|
||||
const viewport = enabledElement.viewport as csTypes.IStackViewport | csTypes.IVolumeViewport;
|
||||
|
||||
const { metadata } = measurement;
|
||||
if (!viewport.isReferenceViewable(metadata, WITH_NAVIGATION)) {
|
||||
console.log("Reference isn't viewable, postponing until updated");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
viewport.setViewReference(metadata);
|
||||
} catch (e) {
|
||||
console.warn('Unable to apply', metadata, e);
|
||||
}
|
||||
|
||||
cs3DTools.annotation.selection.setAnnotationSelected(measurement.uid);
|
||||
// Jump to measurement consumed, remove.
|
||||
cacheJumpToMeasurementEvent?.consume?.();
|
||||
cacheJumpToMeasurementEvent = null;
|
||||
}
|
||||
cs3DTools.annotation.selection.setAnnotationSelected(measurement.uid);
|
||||
event?.consume?.();
|
||||
}
|
||||
|
||||
function _rehydrateSynchronizers(viewportId: string, syncGroupService: any) {
|
||||
|
||||
@@ -215,6 +215,7 @@ function commandsModule({
|
||||
viewportId,
|
||||
displaySetInstanceUID,
|
||||
referencedImageId,
|
||||
options,
|
||||
}) => {
|
||||
const presentations = cornerstoneViewportService.getPresentations(viewportId);
|
||||
const { positionPresentationStore, setPositionPresentation, getPositionPresentationId } =
|
||||
@@ -226,16 +227,18 @@ function commandsModule({
|
||||
([key, value]) => key.includes(displaySetInstanceUID) && value.viewportId === viewportId
|
||||
)?.[0];
|
||||
|
||||
if (previousReferencedDisplaySetStoreKey) {
|
||||
const presentationData = referencedImageId
|
||||
? {
|
||||
...presentations.positionPresentation,
|
||||
viewReference: {
|
||||
referencedImageId,
|
||||
},
|
||||
}
|
||||
: presentations.positionPresentation;
|
||||
// Create presentation data with referencedImageId and options if provided
|
||||
const presentationData = referencedImageId
|
||||
? {
|
||||
...presentations.positionPresentation,
|
||||
viewReference: {
|
||||
referencedImageId,
|
||||
...options,
|
||||
},
|
||||
}
|
||||
: presentations.positionPresentation;
|
||||
|
||||
if (previousReferencedDisplaySetStoreKey) {
|
||||
setPositionPresentation(previousReferencedDisplaySetStoreKey, presentationData);
|
||||
return;
|
||||
}
|
||||
@@ -243,13 +246,12 @@ function commandsModule({
|
||||
// if not found means we have not visited that referencedDisplaySetInstanceUID before
|
||||
// so we need to grab the positionPresentationId directly from the store,
|
||||
// Todo: this is really hacky, we should have a better way for this
|
||||
|
||||
const positionPresentationId = getPositionPresentationId({
|
||||
displaySetInstanceUIDs: [displaySetInstanceUID],
|
||||
viewportId,
|
||||
});
|
||||
|
||||
setPositionPresentation(positionPresentationId, presentations.positionPresentation);
|
||||
setPositionPresentation(positionPresentationId, presentationData);
|
||||
},
|
||||
getNearbyToolData({ nearbyToolData, element, canvasCoordinates }) {
|
||||
return nearbyToolData ?? cstUtils.getAnnotationNearPoint(element, canvasCoordinates);
|
||||
|
||||
@@ -173,9 +173,6 @@ const cornerstoneExtension: Types.Extensions.Extension = {
|
||||
getHangingProtocolModule,
|
||||
getViewportModule({ servicesManager, commandsManager }) {
|
||||
const ExtendedOHIFCornerstoneViewport = props => {
|
||||
// const onNewImageHandler = jumpData => {
|
||||
// commandsManager.runCommand('jumpToImage', jumpData);
|
||||
// };
|
||||
const { toolbarService } = servicesManager.services;
|
||||
|
||||
return (
|
||||
@@ -192,6 +189,7 @@ const cornerstoneExtension: Types.Extensions.Extension = {
|
||||
{
|
||||
name: 'cornerstone',
|
||||
component: ExtendedOHIFCornerstoneViewport,
|
||||
isReferenceViewable: props => utils.isReferenceViewable({ ...props, servicesManager }),
|
||||
},
|
||||
];
|
||||
},
|
||||
|
||||
@@ -92,6 +92,7 @@ export default async function init({
|
||||
hangingProtocolService,
|
||||
viewportGridService,
|
||||
segmentationService,
|
||||
measurementService,
|
||||
} = servicesManager.services;
|
||||
|
||||
window.services = servicesManager.services;
|
||||
@@ -177,6 +178,17 @@ export default async function init({
|
||||
initCineService(servicesManager);
|
||||
initStudyPrefetcherService(servicesManager);
|
||||
|
||||
[
|
||||
measurementService.EVENTS.JUMP_TO_MEASUREMENT_LAYOUT,
|
||||
measurementService.EVENTS.JUMP_TO_MEASUREMENT_VIEWPORT,
|
||||
].forEach(event => {
|
||||
measurementService.subscribe(event, evt => {
|
||||
const { measurement } = evt;
|
||||
const { uid: annotationUID } = measurement;
|
||||
cornerstoneTools.annotation.selection.setAnnotationSelected(annotationUID, true);
|
||||
});
|
||||
});
|
||||
|
||||
segmentationService.subscribeDebounced(
|
||||
segmentationService.EVENTS.SEGMENTATION_DATA_MODIFIED,
|
||||
async ({ segmentationId }) => {
|
||||
|
||||
@@ -38,7 +38,7 @@ const EVENTS = {
|
||||
VIEWPORT_VOLUMES_CHANGED: 'event::cornerstoneViewportService:viewportVolumesChanged',
|
||||
};
|
||||
|
||||
export const WITH_NAVIGATION = { withNavigation: true, withOrientation: true };
|
||||
export const WITH_NAVIGATION = { withNavigation: true, withOrientation: false };
|
||||
|
||||
/**
|
||||
* Handles cornerstone viewport logic including enabling, disabling, and
|
||||
@@ -851,14 +851,18 @@ class CornerstoneViewportService extends PubSubService implements IViewportServi
|
||||
viewport.setProperties(properties, volumeId);
|
||||
});
|
||||
|
||||
viewport.render();
|
||||
|
||||
this.setPresentations(viewport.id, presentations, viewportInfo);
|
||||
|
||||
const imageIndex = this._getInitialImageIndexForViewport(viewportInfo);
|
||||
if (!presentations.positionPresentation) {
|
||||
const imageIndex = this._getInitialImageIndexForViewport(viewportInfo);
|
||||
|
||||
if (imageIndex !== undefined) {
|
||||
csUtils.jumpToSlice(viewport.element, {
|
||||
imageIndex,
|
||||
});
|
||||
if (imageIndex !== undefined) {
|
||||
csUtils.jumpToSlice(viewport.element, {
|
||||
imageIndex,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
viewport.render();
|
||||
@@ -1177,6 +1181,20 @@ class CornerstoneViewportService extends PubSubService implements IViewportServi
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the display sets for a given viewport
|
||||
* @param viewportId - The ID of the viewport to get display sets for
|
||||
* @returns Array of display sets for the viewport
|
||||
*/
|
||||
public getViewportDisplaySets(viewportId: string): OhifTypes.DisplaySet[] {
|
||||
const { displaySetService } = this.servicesManager.services;
|
||||
const displaySetInstanceUIDs = this.viewportsDisplaySets.get(viewportId) || [];
|
||||
|
||||
return displaySetInstanceUIDs
|
||||
.map(uid => displaySetService.getDisplaySetByUID(uid))
|
||||
.filter(Boolean);
|
||||
}
|
||||
}
|
||||
|
||||
export default CornerstoneViewportService;
|
||||
@@ -1,7 +1,9 @@
|
||||
import { handleSegmentChange } from './segmentUtils';
|
||||
import { isReferenceViewable } from './isReferenceViewable';
|
||||
|
||||
const utils = {
|
||||
handleSegmentChange,
|
||||
isReferenceViewable,
|
||||
};
|
||||
|
||||
export default utils;
|
||||
@@ -0,0 +1,98 @@
|
||||
import { vec3 } from 'gl-matrix';
|
||||
import { Enums } from '@cornerstonejs/core';
|
||||
|
||||
import OrientationAxis = Enums.OrientationAxis;
|
||||
|
||||
export const isReferenceViewable = ({
|
||||
viewportId,
|
||||
reference,
|
||||
viewportOptions,
|
||||
servicesManager,
|
||||
}) => {
|
||||
const { cornerstoneViewportService, displaySetService } = servicesManager.services;
|
||||
|
||||
if (!viewportOptions) {
|
||||
const viewport = cornerstoneViewportService.getCornerstoneViewport(viewportId);
|
||||
|
||||
// we can make a customization for this to allow
|
||||
const isViewable = viewport.isReferenceViewable(reference, {
|
||||
withNavigation: true,
|
||||
});
|
||||
|
||||
return isViewable;
|
||||
}
|
||||
|
||||
if (viewportOptions.viewportType === 'stack') {
|
||||
// we only need the viewport to include the referenced imageId
|
||||
const displaySet = displaySetService.getDisplaySetByUID(reference.displaySetInstanceUID);
|
||||
const imageIds = displaySet.instances.map(instance => instance.imageId);
|
||||
return imageIds.includes(reference.referencedImageId);
|
||||
}
|
||||
|
||||
// for the volume viewports, we need to check orientation
|
||||
const { orientation } = viewportOptions;
|
||||
|
||||
// Todo: handle hanging protocols that have acquisition orientation
|
||||
const closestOrientation = getClosestOrientationFromIOP(
|
||||
displaySetService,
|
||||
reference.displaySetInstanceUID
|
||||
);
|
||||
|
||||
return closestOrientation === orientation;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the plane (orientation) to which the ImageOrientationPatient is most closely aligned
|
||||
*
|
||||
* @param displaySetService
|
||||
* @param displaySetInstanceUID
|
||||
* @returns orientation
|
||||
*/
|
||||
export default function getClosestOrientationFromIOP(
|
||||
displaySetService,
|
||||
displaySetInstanceUID
|
||||
): OrientationAxis {
|
||||
const displaySet = displaySetService.getDisplaySetByUID(displaySetInstanceUID);
|
||||
const imageOrientationPatient = displaySet.instances[0].ImageOrientationPatient as Array<number>;
|
||||
// ImageOrientationPatient must be an array of length 6.
|
||||
if (imageOrientationPatient?.length !== 6) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Take cross product to get vector coming "out" of image plane
|
||||
const rowCosineVec = vec3.fromValues(
|
||||
imageOrientationPatient[0],
|
||||
imageOrientationPatient[1],
|
||||
imageOrientationPatient[2]
|
||||
);
|
||||
const colCosineVec = vec3.fromValues(
|
||||
imageOrientationPatient[3],
|
||||
imageOrientationPatient[4],
|
||||
imageOrientationPatient[5]
|
||||
);
|
||||
const scanAxisNormal = vec3.cross(vec3.create(), rowCosineVec, colCosineVec);
|
||||
|
||||
// Define the reference vectors for axial, coronal, and sagittal planes
|
||||
const unitVectors = {
|
||||
[OrientationAxis.AXIAL]: vec3.fromValues(0, 0, 1),
|
||||
[OrientationAxis.CORONAL]: vec3.fromValues(0, 1, 0),
|
||||
[OrientationAxis.SAGITTAL]: vec3.fromValues(1, 0, 0),
|
||||
};
|
||||
|
||||
// Compute dot products for each reference plane
|
||||
// Because all vectors are normalized, dot product is bounded between -1 and 1
|
||||
let maxDot = 0;
|
||||
let maxOrientation: string = '';
|
||||
for (const [k, v] of Object.entries(unitVectors)) {
|
||||
// Absolute value of dot product because we only care about alignment with the axis
|
||||
// For example, dot product of -1 for a given axis means perfect alignment
|
||||
// but the image is pointing in the "opposite" direction
|
||||
const res = Math.abs(vec3.dot(scanAxisNormal, v));
|
||||
if (res > maxDot) {
|
||||
maxDot = res;
|
||||
maxOrientation = k;
|
||||
}
|
||||
}
|
||||
|
||||
return maxOrientation as OrientationAxis;
|
||||
}
|
||||
@@ -82,7 +82,7 @@ function ViewerLayout({
|
||||
);
|
||||
}
|
||||
|
||||
return { entry, content: entry.component };
|
||||
return { entry };
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
@@ -107,6 +107,7 @@ function ViewerLayout({
|
||||
|
||||
return {
|
||||
component: entry.component,
|
||||
isReferenceViewable: entry.isReferenceViewable,
|
||||
displaySetsToDisplay: viewportComponent.displaySetsToDisplay,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -14,6 +14,7 @@ const getViewportModule = ({
|
||||
{
|
||||
name: 'chartViewport',
|
||||
component: LineChartViewport,
|
||||
isReferenceViewable: () => false,
|
||||
},
|
||||
];
|
||||
};
|
||||
|
||||
@@ -32,8 +32,8 @@
|
||||
"start": "yarn run dev"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@cornerstonejs/core": "^3.7.16",
|
||||
"@cornerstonejs/tools": "^3.7.16",
|
||||
"@cornerstonejs/core": "^3.7.17",
|
||||
"@cornerstonejs/tools": "^3.7.17",
|
||||
"@ohif/core": "3.10.0-beta.140",
|
||||
"@ohif/extension-cornerstone-dicom-sr": "3.10.0-beta.140",
|
||||
"@ohif/extension-default": "3.10.0-beta.140",
|
||||
|
||||
+12
-3
@@ -268,12 +268,20 @@ function TrackedMeasurementsContextProvider(
|
||||
displaySet.SOPClassHandlerId === SR_SOPCLASSHANDLERID &&
|
||||
displaySet.isRehydratable === true
|
||||
) {
|
||||
console.log('sending event...', trackedMeasurements);
|
||||
sendTrackedMeasurementsEvent('PROMPT_HYDRATE_SR', {
|
||||
const params = {
|
||||
displaySetInstanceUID: displaySet.displaySetInstanceUID,
|
||||
SeriesInstanceUID: displaySet.SeriesInstanceUID,
|
||||
viewportId: activeViewportId,
|
||||
});
|
||||
};
|
||||
|
||||
// Check if we should bypass the confirmation prompt
|
||||
const disableConfirmationPrompts = appConfig?.disableConfirmationPrompts;
|
||||
|
||||
if (disableConfirmationPrompts) {
|
||||
sendTrackedMeasurementsEvent('HYDRATE_SR', params);
|
||||
} else {
|
||||
sendTrackedMeasurementsEvent('PROMPT_HYDRATE_SR', params);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -284,6 +292,7 @@ function TrackedMeasurementsContextProvider(
|
||||
sendTrackedMeasurementsEvent,
|
||||
servicesManager.services,
|
||||
viewports,
|
||||
appConfig,
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
-1
@@ -57,7 +57,6 @@ const machineConfiguration = {
|
||||
actions: ['setPreviousState'],
|
||||
},
|
||||
],
|
||||
// Unused? We may only do PROMPT_HYDRATE_SR now?
|
||||
SET_TRACKED_SERIES: [
|
||||
{
|
||||
target: 'tracking',
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import React from 'react';
|
||||
import { utils } from '@ohif/extension-cornerstone';
|
||||
|
||||
const Component = React.lazy(() => {
|
||||
return import(/* webpackPrefetch: true */ './viewports/TrackedCornerstoneViewport');
|
||||
@@ -28,6 +29,7 @@ function getViewportModule({ servicesManager, commandsManager, extensionManager
|
||||
{
|
||||
name: 'cornerstone-tracked',
|
||||
component: ExtendedOHIFCornerstoneTrackingViewport,
|
||||
isReferenceViewable: props => utils.isReferenceViewable({ ...props, servicesManager }),
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user