fix: image slices are displayed in reverse order, if we switch from common layout to 2D MPR (#5022)
@ -0,0 +1,6 @@
|
|||||||
|
export default {
|
||||||
|
instanceSortingCriteria: {
|
||||||
|
sortFunctions: {},
|
||||||
|
defaultSortFunctionName: '',
|
||||||
|
},
|
||||||
|
};
|
||||||
@ -22,6 +22,7 @@ import userPreferencesCustomization from './customizations/userPreferencesCustom
|
|||||||
import reportDialogCustomization from './customizations/reportDialogCustomization';
|
import reportDialogCustomization from './customizations/reportDialogCustomization';
|
||||||
import hotkeyBindingsCustomization from './customizations/hotkeyBindingsCustomization';
|
import hotkeyBindingsCustomization from './customizations/hotkeyBindingsCustomization';
|
||||||
import onboardingCustomization from './customizations/onboardingCustomization';
|
import onboardingCustomization from './customizations/onboardingCustomization';
|
||||||
|
import instanceSortingCriteriaCustomization from './customizations/instanceSortingCriteriaCustomization';
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* Note: this is an example of how the customization module can be used
|
* Note: this is an example of how the customization module can be used
|
||||||
@ -69,6 +70,7 @@ export default function getCustomizationModule({ servicesManager, extensionManag
|
|||||||
...reportDialogCustomization,
|
...reportDialogCustomization,
|
||||||
...hotkeyBindingsCustomization,
|
...hotkeyBindingsCustomization,
|
||||||
...onboardingCustomization,
|
...onboardingCustomization,
|
||||||
|
...instanceSortingCriteriaCustomization,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|||||||
@ -127,7 +127,10 @@ const makeDisplaySet = instances => {
|
|||||||
FrameOfReferenceUID: instance.FrameOfReferenceUID,
|
FrameOfReferenceUID: instance.FrameOfReferenceUID,
|
||||||
});
|
});
|
||||||
|
|
||||||
imageSet.sortBy(instancesSortCriteria.default);
|
const { servicesManager } = appContext;
|
||||||
|
const { customizationService } = servicesManager.services;
|
||||||
|
|
||||||
|
imageSet.sort(customizationService);
|
||||||
|
|
||||||
// Include the first image instance number (after sorted)
|
// Include the first image instance number (after sorted)
|
||||||
/*imageSet.setAttribute(
|
/*imageSet.setAttribute(
|
||||||
|
|||||||
@ -1,7 +1,9 @@
|
|||||||
import { vec3 } from 'gl-matrix';
|
import { vec3 } from 'gl-matrix';
|
||||||
import toNumber from '@ohif/core/src/utils/toNumber';
|
import toNumber from '@ohif/core/src/utils/toNumber';
|
||||||
import { _getPerpendicularDistance } from '@ohif/core/src/utils/isDisplaySetReconstructable';
|
import { _getPerpendicularDistance } from '@ohif/core/src/utils/isDisplaySetReconstructable';
|
||||||
import calculateScanAxisNormal from '../calculateScanAxisNormal';
|
import { utils } from '@ohif/core';
|
||||||
|
|
||||||
|
const { calculateScanAxisNormal } = utils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if there is a position shift between consecutive frames
|
* Checks if there is a position shift between consecutive frames
|
||||||
|
|||||||
@ -18,32 +18,32 @@ describe('OHIF Cornerstone Hotkeys', () => {
|
|||||||
|
|
||||||
it('checks if hotkeys "R" and "L" can rotate the image', () => {
|
it('checks if hotkeys "R" and "L" can rotate the image', () => {
|
||||||
cy.get('body').type('R');
|
cy.get('body').type('R');
|
||||||
cy.get('@viewportInfoMidLeft').should('contains.text', 'P');
|
cy.get('@viewportInfoMidLeft').should('contains.text', 'F');
|
||||||
cy.get('@viewportInfoMidTop').should('contains.text', 'R');
|
cy.get('@viewportInfoMidTop').should('contains.text', 'A');
|
||||||
// Hotkey L
|
// Hotkey L
|
||||||
cy.get('body').type('L');
|
cy.get('body').type('L');
|
||||||
cy.get('@viewportInfoMidLeft').should('contains.text', 'R');
|
cy.get('@viewportInfoMidLeft').should('contains.text', 'A');
|
||||||
cy.get('@viewportInfoMidTop').should('contains.text', 'A');
|
cy.get('@viewportInfoMidTop').should('contains.text', 'H');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('checks if hotkeys "ArrowUp" and "ArrowDown" can navigate in the stack', () => {
|
it('checks if hotkeys "ArrowUp" and "ArrowDown" can navigate in the stack', () => {
|
||||||
// Hotkey ArrowDown
|
// Hotkey ArrowDown
|
||||||
cy.get('body').type('{downarrow}');
|
cy.get('body').type('{downarrow}');
|
||||||
cy.get('@viewportInfoBottomRight').should('contains.text', 'I:2 (2/26)');
|
cy.get('@viewportInfoBottomRight').should('contains.text', 'I:11 (2/26)');
|
||||||
// Hotkey ArrowUp
|
// Hotkey ArrowUp
|
||||||
cy.get('body').type('{uparrow}');
|
cy.get('body').type('{uparrow}');
|
||||||
cy.get('@viewportInfoBottomRight').should('contains.text', 'I:1 (1/26)');
|
cy.get('@viewportInfoBottomRight').should('contains.text', 'I:10 (1/26)');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('checks if hotkeys "V" and "H" can flip the image', () => {
|
it('checks if hotkeys "V" and "H" can flip the image', () => {
|
||||||
// Hotkey H
|
// Hotkey H
|
||||||
cy.get('body').type('h');
|
cy.get('body').type('h');
|
||||||
cy.get('@viewportInfoMidLeft').should('contains.text', 'L');
|
cy.get('@viewportInfoMidLeft').should('contains.text', 'P');
|
||||||
cy.get('@viewportInfoMidTop').should('contains.text', 'A');
|
cy.get('@viewportInfoMidTop').should('contains.text', 'H');
|
||||||
// Hotkey V
|
// Hotkey V
|
||||||
cy.get('body').type('v');
|
cy.get('body').type('v');
|
||||||
cy.get('@viewportInfoMidLeft').should('contains.text', 'L');
|
cy.get('@viewportInfoMidLeft').should('contains.text', 'P');
|
||||||
cy.get('@viewportInfoMidTop').should('contains.text', 'P');
|
cy.get('@viewportInfoMidTop').should('contains.text', 'F');
|
||||||
});
|
});
|
||||||
|
|
||||||
// it('checks if hotkeys "+", "-" and "=" can zoom in, out and fit to viewport', () => {
|
// it('checks if hotkeys "+", "-" and "=" can zoom in, out and fit to viewport', () => {
|
||||||
@ -68,13 +68,13 @@ describe('OHIF Cornerstone Hotkeys', () => {
|
|||||||
it('checks if hotkey "SPACEBAR" can reset the image', () => {
|
it('checks if hotkey "SPACEBAR" can reset the image', () => {
|
||||||
// Press multiples hotkeys
|
// Press multiples hotkeys
|
||||||
cy.get('body').type('v+++i');
|
cy.get('body').type('v+++i');
|
||||||
cy.get('@viewportInfoMidLeft').should('contains.text', 'R');
|
cy.get('@viewportInfoMidLeft').should('contains.text', 'A');
|
||||||
cy.get('@viewportInfoMidTop').should('contains.text', 'P');
|
cy.get('@viewportInfoMidTop').should('contains.text', 'F');
|
||||||
|
|
||||||
// Hotkey SPACEBAR
|
// Hotkey SPACEBAR
|
||||||
cy.get('body').type(' ');
|
cy.get('body').type(' ');
|
||||||
cy.get('@viewportInfoMidLeft').should('contains.text', 'R');
|
cy.get('@viewportInfoMidLeft').should('contains.text', 'A');
|
||||||
cy.get('@viewportInfoMidTop').should('contains.text', 'A');
|
cy.get('@viewportInfoMidTop').should('contains.text', 'H');
|
||||||
});
|
});
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
@ -85,7 +85,7 @@ describe('OHIF Cornerstone Toolbar', () => {
|
|||||||
// The exact text is slightly dependent on the viewport resolution, so leave a range
|
// The exact text is slightly dependent on the viewport resolution, so leave a range
|
||||||
cy.get('@viewportInfoBottomLeft').should($txt => {
|
cy.get('@viewportInfoBottomLeft').should($txt => {
|
||||||
const text = $txt.text();
|
const text = $txt.text();
|
||||||
expect(text).to.include('L:479');
|
expect(text).to.include('L:288');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -143,7 +143,7 @@ describe('OHIF Cornerstone Toolbar', () => {
|
|||||||
//Click on reset button
|
//Click on reset button
|
||||||
cy.resetViewport();
|
cy.resetViewport();
|
||||||
|
|
||||||
const expectedText = 'W:958L:479';
|
const expectedText = 'W:576L:288';
|
||||||
cy.get('@viewportInfoBottomLeft').should('have.text', expectedText);
|
cy.get('@viewportInfoBottomLeft').should('have.text', expectedText);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -412,8 +412,8 @@ describe('OHIF Cornerstone Toolbar', () => {
|
|||||||
});
|
});
|
||||||
*/
|
*/
|
||||||
it('check if Flip tool will flip the image in the viewport', () => {
|
it('check if Flip tool will flip the image in the viewport', () => {
|
||||||
cy.get('@viewportInfoMidLeft').should('contains.text', 'R');
|
cy.get('@viewportInfoMidLeft').should('contains.text', 'A');
|
||||||
cy.get('@viewportInfoMidTop').should('contains.text', 'A');
|
cy.get('@viewportInfoMidTop').should('contains.text', 'H');
|
||||||
|
|
||||||
//Click on More button
|
//Click on More button
|
||||||
cy.get('@moreBtnSecondary').click();
|
cy.get('@moreBtnSecondary').click();
|
||||||
@ -421,8 +421,8 @@ describe('OHIF Cornerstone Toolbar', () => {
|
|||||||
//Click on Flip button
|
//Click on Flip button
|
||||||
cy.get('[data-cy="flipHorizontal"]').click();
|
cy.get('[data-cy="flipHorizontal"]').click();
|
||||||
cy.waitDicomImage();
|
cy.waitDicomImage();
|
||||||
cy.get('@viewportInfoMidLeft').should('contains.text', 'L');
|
cy.get('@viewportInfoMidLeft').should('contains.text', 'P');
|
||||||
cy.get('@viewportInfoMidTop').should('contains.text', 'A');
|
cy.get('@viewportInfoMidTop').should('contains.text', 'H');
|
||||||
});
|
});
|
||||||
|
|
||||||
// it('checks if stack sync is preserved on new display set and uses FOR', () => {
|
// it('checks if stack sync is preserved on new display set and uses FOR', () => {
|
||||||
|
|||||||
@ -1,15 +1,16 @@
|
|||||||
|
import { Types as csTypes } from '@cornerstonejs/core';
|
||||||
import guid from '../utils/guid.js';
|
import guid from '../utils/guid.js';
|
||||||
import { Vector3 } from 'cornerstone-math';
|
import {
|
||||||
|
instancesSortCriteria,
|
||||||
|
isValidForPositionSort,
|
||||||
|
sortImagesByPatientPosition,
|
||||||
|
} from '../utils/sortStudy';
|
||||||
|
|
||||||
type Attributes = Record<string, unknown>;
|
type Attributes = Record<string, unknown>;
|
||||||
type Image = {
|
export type Image = {
|
||||||
StudyInstanceUID?: string;
|
StudyInstanceUID?: string;
|
||||||
getData(): {
|
ImagePositionPatient?: csTypes.Point3;
|
||||||
metadata: {
|
ImageOrientationPatient?: csTypes.Point3;
|
||||||
ImagePositionPatient: number[];
|
|
||||||
ImageOrientationPatient: number[];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -79,63 +80,46 @@ class ImageSet {
|
|||||||
return this.images[index];
|
return this.images[index];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default image sorting. Sorts by the following (in order of priority)
|
||||||
|
* 1. Image position (if ImagePositionPatient and ImageOrientationPatient are defined)
|
||||||
|
* 2. Sort by a provided sortingCallback Criteria
|
||||||
|
* Note: Images are sorted in-place and a reference to the sorted image array is returned.
|
||||||
|
*
|
||||||
|
* @returns images - reference to images after sorting
|
||||||
|
*/
|
||||||
|
sort(customizationService): Image[] {
|
||||||
|
// Check instanceSort customization
|
||||||
|
const customizedSortingCriteria =
|
||||||
|
customizationService.getCustomization('instanceSortingCriteria');
|
||||||
|
const combinedSortFunctions = Object.assign(
|
||||||
|
{},
|
||||||
|
instancesSortCriteria,
|
||||||
|
customizedSortingCriteria.sortFunctions
|
||||||
|
);
|
||||||
|
const userSpecifiedCriteria = customizedSortingCriteria.defaultSortFunctionName;
|
||||||
|
// Prefer customized sort function when available
|
||||||
|
if (typeof combinedSortFunctions[userSpecifiedCriteria] === 'function') {
|
||||||
|
return this.images.sort(combinedSortFunctions[userSpecifiedCriteria]);
|
||||||
|
}
|
||||||
|
// If image position patient is not available, sort by InstanceNumber
|
||||||
|
if (!isValidForPositionSort(this.images)) {
|
||||||
|
return this.images.sort(instancesSortCriteria.sortByInstanceNumber);
|
||||||
|
}
|
||||||
|
// Do image position patient sorting as default sort
|
||||||
|
return sortImagesByPatientPosition(this.images);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sort using the provided callback function.
|
||||||
|
* Note: Images are sorted in-place and a reference to the sorted image array is returned.
|
||||||
|
*
|
||||||
|
* @param sortingCallback - sorting function
|
||||||
|
* @returns images - reference to images after sorting
|
||||||
|
*/
|
||||||
sortBy(sortingCallback: (a: Image, b: Image) => number): Image[] {
|
sortBy(sortingCallback: (a: Image, b: Image) => number): Image[] {
|
||||||
return this.images.sort(sortingCallback);
|
return this.images.sort(sortingCallback);
|
||||||
}
|
}
|
||||||
|
|
||||||
sortByImagePositionPatient(): void {
|
|
||||||
const images = this.images;
|
|
||||||
const referenceImagePositionPatient = _getImagePositionPatient(images[0]);
|
|
||||||
|
|
||||||
const refIppVec = new Vector3(
|
|
||||||
referenceImagePositionPatient[0],
|
|
||||||
referenceImagePositionPatient[1],
|
|
||||||
referenceImagePositionPatient[2]
|
|
||||||
);
|
|
||||||
|
|
||||||
const ImageOrientationPatient = _getImageOrientationPatient(images[0]);
|
|
||||||
|
|
||||||
const scanAxisNormal = new Vector3(
|
|
||||||
ImageOrientationPatient[0],
|
|
||||||
ImageOrientationPatient[1],
|
|
||||||
ImageOrientationPatient[2]
|
|
||||||
).cross(
|
|
||||||
new Vector3(
|
|
||||||
ImageOrientationPatient[3],
|
|
||||||
ImageOrientationPatient[4],
|
|
||||||
ImageOrientationPatient[5]
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
const distanceImagePairs = images.map(function (image: Image) {
|
|
||||||
const ippVec = new Vector3(..._getImagePositionPatient(image));
|
|
||||||
const positionVector = refIppVec.clone().sub(ippVec);
|
|
||||||
const distance = positionVector.dot(scanAxisNormal);
|
|
||||||
|
|
||||||
return {
|
|
||||||
distance,
|
|
||||||
image,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
distanceImagePairs.sort(function (a, b) {
|
|
||||||
return b.distance - a.distance;
|
|
||||||
});
|
|
||||||
|
|
||||||
const sortedImages = distanceImagePairs.map(a => a.image);
|
|
||||||
|
|
||||||
images.sort(function (a, b) {
|
|
||||||
return sortedImages.indexOf(a) - sortedImages.indexOf(b);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function _getImagePositionPatient(image) {
|
|
||||||
return image.getData().metadata.ImagePositionPatient;
|
|
||||||
}
|
|
||||||
|
|
||||||
function _getImageOrientationPatient(image) {
|
|
||||||
return image.getData().metadata.ImageOrientationPatient;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default ImageSet;
|
export default ImageSet;
|
||||||
|
|||||||
@ -43,6 +43,7 @@ import { createStudyBrowserTabs } from './createStudyBrowserTabs';
|
|||||||
import { sopClassDictionary } from './sopClassDictionary';
|
import { sopClassDictionary } from './sopClassDictionary';
|
||||||
import * as MeasurementFilters from './measurementFilters';
|
import * as MeasurementFilters from './measurementFilters';
|
||||||
import getClosestOrientationFromIOP from './getClosestOrientationFromIOP';
|
import getClosestOrientationFromIOP from './getClosestOrientationFromIOP';
|
||||||
|
import calculateScanAxisNormal from './calculateScanAxisNormal';
|
||||||
// Commented out unused functionality.
|
// Commented out unused functionality.
|
||||||
// Need to implement new mechanism for derived displaySets using the displaySetManager.
|
// Need to implement new mechanism for derived displaySets using the displaySetManager.
|
||||||
|
|
||||||
@ -91,6 +92,7 @@ const utils = {
|
|||||||
createStudyBrowserTabs,
|
createStudyBrowserTabs,
|
||||||
MeasurementFilters,
|
MeasurementFilters,
|
||||||
getClosestOrientationFromIOP,
|
getClosestOrientationFromIOP,
|
||||||
|
calculateScanAxisNormal,
|
||||||
};
|
};
|
||||||
|
|
||||||
export {
|
export {
|
||||||
|
|||||||
@ -1,4 +1,6 @@
|
|||||||
|
import { vec3 } from 'gl-matrix';
|
||||||
import isLowPriorityModality from './isLowPriorityModality';
|
import isLowPriorityModality from './isLowPriorityModality';
|
||||||
|
import calculateScanAxisNormal from './calculateScanAxisNormal';
|
||||||
|
|
||||||
const compareSeriesDateTime = (a, b) => {
|
const compareSeriesDateTime = (a, b) => {
|
||||||
const seriesDateA = Date.parse(`${a.seriesDate ?? a.SeriesDate} ${a.seriesTime ?? a.SeriesTime}`);
|
const seriesDateA = Date.parse(`${a.seriesDate ?? a.SeriesDate} ${a.seriesTime ?? a.SeriesTime}`);
|
||||||
@ -41,8 +43,7 @@ const seriesSortCriteria = {
|
|||||||
seriesInfoSortingCriteria,
|
seriesInfoSortingCriteria,
|
||||||
};
|
};
|
||||||
|
|
||||||
const instancesSortCriteria = {
|
const sortByInstanceNumber = (a, b) => {
|
||||||
default: (a, b) => {
|
|
||||||
// Sort by InstanceNumber (0020,0013)
|
// Sort by InstanceNumber (0020,0013)
|
||||||
const aInstance = parseInt(a.InstanceNumber) || 0;
|
const aInstance = parseInt(a.InstanceNumber) || 0;
|
||||||
const bInstance = parseInt(b.InstanceNumber) || 0;
|
const bInstance = parseInt(b.InstanceNumber) || 0;
|
||||||
@ -54,7 +55,11 @@ const instancesSortCriteria = {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
return a.SOPInstanceUID < b.SOPInstanceUID ? -1 : 1;
|
return a.SOPInstanceUID < b.SOPInstanceUID ? -1 : 1;
|
||||||
},
|
};
|
||||||
|
|
||||||
|
const instancesSortCriteria = {
|
||||||
|
default: sortByInstanceNumber,
|
||||||
|
sortByInstanceNumber,
|
||||||
};
|
};
|
||||||
|
|
||||||
const sortingCriteria = {
|
const sortingCriteria = {
|
||||||
@ -128,6 +133,55 @@ export default function sortStudy(
|
|||||||
return study;
|
return study;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isValidForPositionSort(images): boolean {
|
||||||
|
if (images.length <= 1) {
|
||||||
|
return false; // No need to sort if there's only one image
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use the first image as a reference
|
||||||
|
const referenceImagePositionPatient = images[0].ImagePositionPatient;
|
||||||
|
const imageOrientationPatient = images[0].ImageOrientationPatient;
|
||||||
|
|
||||||
|
if (!referenceImagePositionPatient || !imageOrientationPatient) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sort by image position, calculated using imageOrientationPatient and ImagePositionPatient
|
||||||
|
* If imageOrientationPatient or ImagePositionPatient is not available, Images will be sorted by the provided sortingCriteria
|
||||||
|
* Note: Images are sorted in-place and a reference to the sorted image array is returned.
|
||||||
|
*
|
||||||
|
* @returns images - reference to images after sorting
|
||||||
|
*/
|
||||||
|
const sortImagesByPatientPosition = images => {
|
||||||
|
const referenceImagePositionPatient = images[0].ImagePositionPatient;
|
||||||
|
const imageOrientationPatient = images[0].ImageOrientationPatient;
|
||||||
|
|
||||||
|
// Calculate the scan axis normal using the cross product
|
||||||
|
const scanAxisNormal = calculateScanAxisNormal(imageOrientationPatient);
|
||||||
|
|
||||||
|
// Compute distances from each image to the reference image
|
||||||
|
const distanceInstancePairs = images.map(image => {
|
||||||
|
const imagePositionPatient = image.ImagePositionPatient;
|
||||||
|
const deltaVector = vec3.create();
|
||||||
|
const distance = vec3.dot(
|
||||||
|
scanAxisNormal,
|
||||||
|
vec3.subtract(deltaVector, imagePositionPatient, referenceImagePositionPatient)
|
||||||
|
);
|
||||||
|
return { distance, image };
|
||||||
|
});
|
||||||
|
// Sort images based on the computed distances
|
||||||
|
distanceInstancePairs.sort((a, b) => b.distance - a.distance);
|
||||||
|
// Reorder the images in the original array
|
||||||
|
for (const [index, item] of distanceInstancePairs.entries()) {
|
||||||
|
images[index] = item.image;
|
||||||
|
}
|
||||||
|
|
||||||
|
return images;
|
||||||
|
};
|
||||||
|
|
||||||
export {
|
export {
|
||||||
sortStudy,
|
sortStudy,
|
||||||
sortStudySeries,
|
sortStudySeries,
|
||||||
@ -135,4 +189,6 @@ export {
|
|||||||
sortingCriteria,
|
sortingCriteria,
|
||||||
seriesSortCriteria,
|
seriesSortCriteria,
|
||||||
instancesSortCriteria,
|
instancesSortCriteria,
|
||||||
|
isValidForPositionSort,
|
||||||
|
sortImagesByPatientPosition,
|
||||||
};
|
};
|
||||||
|
|||||||
@ -26,3 +26,17 @@ summary: Migration guide for OHIF 3.11 additional changes
|
|||||||
+ extensionsManager
|
+ extensionsManager
|
||||||
+ });
|
+ });
|
||||||
```
|
```
|
||||||
|
|
||||||
|
**Images sort by position patient**
|
||||||
|
|
||||||
|
The ImageSet sort has been modified: images are now sorted by ImagePositionPatient by default. If ImagePositionPatient is not available, the sort will fall back to InstanceNumber.
|
||||||
|
|
||||||
|
**To revert to the previous sorting method, you can add the customization instanceSortingCriteria as shown below:**
|
||||||
|
|
||||||
|
```
|
||||||
|
customizationService.setCustomizations({
|
||||||
|
'instanceSortingCriteria': {
|
||||||
|
$set: {defaultSortFunctionName: 'default'},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|||||||
@ -1818,6 +1818,31 @@ window.config = {
|
|||||||
};
|
};
|
||||||
},
|
},
|
||||||
}],
|
}],
|
||||||
|
};
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'instanceSortingCriteria',
|
||||||
|
description: 'Defines the instance sorting criteria to sort the images',
|
||||||
|
default: `{
|
||||||
|
sortFunctions: {},
|
||||||
|
defaultSortFunctionName: '',
|
||||||
|
}`,
|
||||||
|
configuration: `
|
||||||
|
window.config = {
|
||||||
|
// rest of window config
|
||||||
|
customizationService: [
|
||||||
|
{
|
||||||
|
'instanceSortingCriteria': {
|
||||||
|
$set: {
|
||||||
|
sortFunctions: {
|
||||||
|
sort: (a, b) => {}
|
||||||
|
},
|
||||||
|
defaultSortFunctionName: 'sort',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
],
|
||||||
};
|
};
|
||||||
`,
|
`,
|
||||||
},
|
},
|
||||||
|
|||||||
@ -4,7 +4,7 @@ import { visitStudy, checkForScreenshot, screenShotPaths, simulateClicksOnElemen
|
|||||||
test.beforeEach(async ({ page }) => {
|
test.beforeEach(async ({ page }) => {
|
||||||
const studyInstanceUID = '1.3.6.1.4.1.25403.345050719074.3824.20170125095438.5';
|
const studyInstanceUID = '1.3.6.1.4.1.25403.345050719074.3824.20170125095438.5';
|
||||||
const mode = 'viewer';
|
const mode = 'viewer';
|
||||||
await visitStudy(page, studyInstanceUID, mode, 2000);
|
await visitStudy(page, studyInstanceUID, mode, 5000);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should hydrate in MPR correctly', async ({ page }) => {
|
test('should hydrate in MPR correctly', async ({ page }) => {
|
||||||
@ -15,6 +15,8 @@ test('should hydrate in MPR correctly', async ({ page }) => {
|
|||||||
|
|
||||||
await page.locator(':text("S:7")').first().dblclick();
|
await page.locator(':text("S:7")').first().dblclick();
|
||||||
|
|
||||||
|
await page.waitForTimeout(5000);
|
||||||
|
|
||||||
await page.evaluate(() => {
|
await page.evaluate(() => {
|
||||||
// Access cornerstone directly from the window object
|
// Access cornerstone directly from the window object
|
||||||
const cornerstone = window.cornerstone;
|
const cornerstone = window.cornerstone;
|
||||||
@ -34,6 +36,8 @@ test('should hydrate in MPR correctly', async ({ page }) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
await page.waitForTimeout(5000);
|
||||||
|
|
||||||
await page.getByTestId('MeasurementTools-split-button-secondary').click();
|
await page.getByTestId('MeasurementTools-split-button-secondary').click();
|
||||||
await page.getByTestId('Bidirectional').click();
|
await page.getByTestId('Bidirectional').click();
|
||||||
const locator = page.getByTestId('viewport-pane').locator('canvas');
|
const locator = page.getByTestId('viewport-pane').locator('canvas');
|
||||||
@ -80,8 +84,8 @@ test('should hydrate in MPR correctly', async ({ page }) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// wait 2 seconds
|
// wait 5 seconds
|
||||||
await page.waitForTimeout(2000);
|
await page.waitForTimeout(5000);
|
||||||
|
|
||||||
await checkForScreenshot(page, page, screenShotPaths.jumpToMeasurementMPR.scrollAway);
|
await checkForScreenshot(page, page, screenShotPaths.jumpToMeasurementMPR.scrollAway);
|
||||||
|
|
||||||
@ -92,8 +96,8 @@ test('should hydrate in MPR correctly', async ({ page }) => {
|
|||||||
await page.getByTestId('Layout').click();
|
await page.getByTestId('Layout').click();
|
||||||
await page.locator('div').filter({ hasText: /^MPR$/ }).first().click();
|
await page.locator('div').filter({ hasText: /^MPR$/ }).first().click();
|
||||||
|
|
||||||
// wait 2 seconds
|
// wait 5 seconds
|
||||||
await page.waitForTimeout(2000);
|
await page.waitForTimeout(5000);
|
||||||
|
|
||||||
// jump in viewport again
|
// jump in viewport again
|
||||||
await page.getByTestId('data-row').first().click();
|
await page.getByTestId('data-row').first().click();
|
||||||
@ -104,6 +108,8 @@ test('should hydrate in MPR correctly', async ({ page }) => {
|
|||||||
|
|
||||||
await page.locator(':text("S:3")').first().dblclick();
|
await page.locator(':text("S:3")').first().dblclick();
|
||||||
|
|
||||||
|
await page.waitForTimeout(5000);
|
||||||
|
|
||||||
await checkForScreenshot(page, page, screenShotPaths.jumpToMeasurementMPR.changeSeriesInMPR);
|
await checkForScreenshot(page, page, screenShotPaths.jumpToMeasurementMPR.changeSeriesInMPR);
|
||||||
|
|
||||||
await page.getByTestId('data-row').first().click();
|
await page.getByTestId('data-row').first().click();
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 268 KiB After Width: | Height: | Size: 260 KiB |
|
Before Width: | Height: | Size: 218 KiB After Width: | Height: | Size: 218 KiB |
|
Before Width: | Height: | Size: 198 KiB After Width: | Height: | Size: 192 KiB |
|
Before Width: | Height: | Size: 198 KiB After Width: | Height: | Size: 192 KiB |
|
Before Width: | Height: | Size: 218 KiB After Width: | Height: | Size: 218 KiB |
|
Before Width: | Height: | Size: 136 KiB After Width: | Height: | Size: 131 KiB |