chore(prettier): full repo linting and code reformatting once and for all (#3627)

This commit is contained in:
Alireza 2023-09-01 16:19:39 -04:00 committed by GitHub
parent 01500a2d4c
commit 9045ddaedc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
656 changed files with 21737 additions and 20628 deletions

View File

@ -269,8 +269,7 @@ jobs:
git config --global user.name "ohif-bot" git config --global user.name "ohif-bot"
- run: - run:
name: Authenticate with NPM registry name: Authenticate with NPM registry
command: command: echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" > ~/repo/.npmrc
echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" > ~/repo/.npmrc
- run: - run:
name: Increase the event emitter limit name: Increase the event emitter limit
command: | command: |
@ -289,8 +288,7 @@ jobs:
node ./publish-version.mjs node ./publish-version.mjs
- run: - run:
name: Again set the NPM registry (was deleted in the version script) name: Again set the NPM registry (was deleted in the version script)
command: command: echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" > ~/repo/.npmrc
echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" > ~/repo/.npmrc
- run: - run:
name: publish package dist name: publish package dist
command: | command: |
@ -337,8 +335,7 @@ jobs:
- setup_remote_docker: - setup_remote_docker:
docker_layer_caching: false docker_layer_caching: false
- run: - run:
name: name: Build and push Docker image from the master branch (beta releases)
Build and push Docker image from the master branch (beta releases)
command: | command: |
echo $(ls -l) echo $(ls -l)
@ -417,8 +414,8 @@ workflows:
store_artifacts: false store_artifacts: false
working_directory: platform/app working_directory: platform/app
build: build:
yarn test:data && npx cross-env QUICK_BUILD=true yarn test:data && npx cross-env QUICK_BUILD=true APP_CONFIG=config/dicomweb-server.js
APP_CONFIG=config/dicomweb-server.js yarn run build yarn run build
# start server --> verify running --> percy + chrome + cypress # start server --> verify running --> percy + chrome + cypress
command: yarn run test:e2e:dist command: yarn run test:e2e:dist
cache-key: 'yarn-packages-{{ checksum "yarn.lock" }}' cache-key: 'yarn-packages-{{ checksum "yarn.lock" }}'

View File

@ -1,10 +1,5 @@
{ {
"plugins": [ "plugins": ["@typescript-eslint", "import", "eslint-plugin-tsdoc", "prettier"],
"@typescript-eslint",
"import",
"eslint-plugin-tsdoc",
"prettier"
],
"extends": [ "extends": [
"react-app", "react-app",
"eslint:recommended", "eslint:recommended",

View File

@ -39,8 +39,7 @@ body:
attributes: attributes:
label: The current behavior label: The current behavior
description: description:
'A clear and concise description of what happens instead of the expected 'A clear and concise description of what happens instead of the expected behavior.'
behavior.'
validations: validations:
required: true required: true
@ -48,8 +47,7 @@ body:
id: expected_behavior id: expected_behavior
attributes: attributes:
label: The expected behavior label: The expected behavior
description: description: 'A clear and concise description of what you expected to happen.'
'A clear and concise description of what you expected to happen.'
validations: validations:
required: true required: true
@ -81,6 +79,6 @@ body:
- type: markdown - type: markdown
attributes: attributes:
value: > value: >
> :warning: Reports we cannot reproduce are at risk of being marked > :warning: Reports we cannot reproduce are at risk of being marked stale and > closed. The
stale and > closed. The more information you can provide, the more more information you can provide, the more likely we are to look > into and address your
likely we are to look > into and address your issue. issue.

View File

@ -20,17 +20,15 @@ body:
attributes: attributes:
label: 'What feature or change would you like to see made?' label: 'What feature or change would you like to see made?'
description: description:
'Please include as much detail as possible including possibly mock up 'Please include as much detail as possible including possibly mock up screen shots, workflow
screen shots, workflow or logic flow diagrams etc.' or logic flow diagrams etc.'
placeholder: '...' placeholder: '...'
validations: validations:
required: true required: true
- type: textarea - type: textarea
attributes: attributes:
label: 'Why should we prioritize this feature?' label: 'Why should we prioritize this feature?'
description: description: 'Discuss if and how the requested feature interacts with existing features.'
'Discuss if and how the requested feature interacts with existing
features.'
placeholder: '...' placeholder: '...'
validations: validations:
required: true required: true

5
.github/stale.yml vendored
View File

@ -19,8 +19,7 @@ exemptLabels:
staleLabel: 'Stale :baguette_bread:' staleLabel: 'Stale :baguette_bread:'
# Comment to post when marking an issue as stale. Set to `false` to disable # Comment to post when marking an issue as stale. Set to `false` to disable
markComment: > markComment: >
This issue has been automatically marked as stale because it has not had This issue has been automatically marked as stale because it has not had recent activity. It will
recent activity. It will be closed if no further activity occurs. Thank you be closed if no further activity occurs. Thank you for your contributions.
for your contributions.
# Comment to post when closing a stale issue. Set to `false` to disable # Comment to post when closing a stale issue. Set to `false` to disable
closeComment: false closeComment: false

View File

@ -1,10 +1,9 @@
<html> <html>
<head>
<head>
<title>OHIF Viewer: Deploy Preview</title> <title>OHIF Viewer: Deploy Preview</title>
</head> </head>
<body> <body>
<h1>Index of Previews</h1> <h1>Index of Previews</h1>
<ul> <ul>
@ -18,6 +17,5 @@
<a href="/ui">UI: Component Library</a> <a href="/ui">UI: Component Library</a>
</li> </li>
</ul> </ul>
</body> </body>
</html> </html>

View File

@ -1,9 +1,11 @@
{ {
"plugins": ["prettier-plugin-tailwindcss"],
"trailingComma": "es5", "trailingComma": "es5",
"printWidth": 80, "printWidth": 100,
"proseWrap": "always", "proseWrap": "always",
"tabWidth": 2, "tabWidth": 2,
"semi": true, "semi": true,
"singleQuote": true, "singleQuote": true,
"arrowParens": "avoid" "arrowParens": "avoid",
"singleAttributePerLine": true
} }

View File

@ -5,11 +5,11 @@ function excludeNodeModulesExcept(modules) {
if (pathSep == '\\') if (pathSep == '\\')
// must be quoted for use in a regexp: // must be quoted for use in a regexp:
pathSep = '\\\\'; pathSep = '\\\\';
var moduleRegExps = modules.map(function(modName) { var moduleRegExps = modules.map(function (modName) {
return new RegExp('node_modules' + pathSep + modName); return new RegExp('node_modules' + pathSep + modName);
}); });
return function(modulePath) { return function (modulePath) {
if (/node_modules/.test(modulePath)) { if (/node_modules/.test(modulePath)) {
for (var i = 0; i < moduleRegExps.length; i++) for (var i = 0; i < moduleRegExps.length; i++)
if (moduleRegExps[i].test(modulePath)) return false; if (moduleRegExps[i].test(modulePath)) return false;

View File

@ -1,9 +1,7 @@
const autoprefixer = require('autoprefixer'); const autoprefixer = require('autoprefixer');
const path = require('path'); const path = require('path');
const tailwindcss = require('tailwindcss'); const tailwindcss = require('tailwindcss');
const tailwindConfigPath = path.resolve( const tailwindConfigPath = path.resolve('../../platform/app/tailwind.config.js');
'../../platform/app/tailwind.config.js'
);
const MiniCssExtractPlugin = require('mini-css-extract-plugin'); const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const devMode = process.env.NODE_ENV !== 'production'; const devMode = process.env.NODE_ENV !== 'production';

View File

@ -7,8 +7,7 @@ const fs = require('fs');
const webpack = require('webpack'); const webpack = require('webpack');
// ~~ PLUGINS // ~~ PLUGINS
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer') const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
.BundleAnalyzerPlugin;
const TerserJSPlugin = require('terser-webpack-plugin'); const TerserJSPlugin = require('terser-webpack-plugin');
// ~~ PackageJSON // ~~ PackageJSON
@ -26,11 +25,9 @@ const QUICK_BUILD = process.env.QUICK_BUILD;
const BUILD_NUM = process.env.CIRCLE_BUILD_NUM || '0'; const BUILD_NUM = process.env.CIRCLE_BUILD_NUM || '0';
// read from ../version.txt // read from ../version.txt
const VERSION_NUMBER = const VERSION_NUMBER = fs.readFileSync(path.join(__dirname, '../version.txt'), 'utf8') || '';
fs.readFileSync(path.join(__dirname, '../version.txt'), 'utf8') || '';
const COMMIT_HASH = const COMMIT_HASH = fs.readFileSync(path.join(__dirname, '../commit.txt'), 'utf8') || '';
fs.readFileSync(path.join(__dirname, '../commit.txt'), 'utf8') || '';
// //
dotenv.config(); dotenv.config();
@ -105,10 +102,7 @@ module.exports = (env, argv, { SRC_DIR, ENTRY }) => {
alias: { alias: {
// Viewer project // Viewer project
'@': path.resolve(__dirname, '../platform/app/src'), '@': path.resolve(__dirname, '../platform/app/src'),
'@components': path.resolve( '@components': path.resolve(__dirname, '../platform/app/src/components'),
__dirname,
'../platform/app/src/components'
),
'@hooks': path.resolve(__dirname, '../platform/app/src/hooks'), '@hooks': path.resolve(__dirname, '../platform/app/src/hooks'),
'@routes': path.resolve(__dirname, '../platform/app/src/routes'), '@routes': path.resolve(__dirname, '../platform/app/src/routes'),
'@state': path.resolve(__dirname, '../platform/app/src/state'), '@state': path.resolve(__dirname, '../platform/app/src/state'),
@ -135,7 +129,7 @@ module.exports = (env, argv, { SRC_DIR, ENTRY }) => {
fs: false, fs: false,
path: false, path: false,
zlib: false, zlib: false,
"buffer": require.resolve("buffer") buffer: require.resolve('buffer'),
}, },
}, },
plugins: [ plugins: [
@ -150,15 +144,9 @@ module.exports = (env, argv, { SRC_DIR, ENTRY }) => {
'process.env.COMMIT_HASH': JSON.stringify(COMMIT_HASH), 'process.env.COMMIT_HASH': JSON.stringify(COMMIT_HASH),
/* i18n */ /* i18n */
'process.env.USE_LOCIZE': JSON.stringify(process.env.USE_LOCIZE || ''), 'process.env.USE_LOCIZE': JSON.stringify(process.env.USE_LOCIZE || ''),
'process.env.LOCIZE_PROJECTID': JSON.stringify( 'process.env.LOCIZE_PROJECTID': JSON.stringify(process.env.LOCIZE_PROJECTID || ''),
process.env.LOCIZE_PROJECTID || '' 'process.env.LOCIZE_API_KEY': JSON.stringify(process.env.LOCIZE_API_KEY || ''),
), 'process.env.REACT_APP_I18N_DEBUG': JSON.stringify(process.env.REACT_APP_I18N_DEBUG || ''),
'process.env.LOCIZE_API_KEY': JSON.stringify(
process.env.LOCIZE_API_KEY || ''
),
'process.env.REACT_APP_I18N_DEBUG': JSON.stringify(
process.env.REACT_APP_I18N_DEBUG || ''
),
}), }),
new webpack.ProvidePlugin({ new webpack.ProvidePlugin({
Buffer: ['buffer', 'Buffer'], Buffer: ['buffer', 'Buffer'],

View File

@ -3,11 +3,7 @@ const { extendDefaultPlugins } = require('svgo');
module.exports = { module.exports = {
babelrcRoots: ['./platform/*', './extensions/*', './modes/*'], babelrcRoots: ['./platform/*', './extensions/*', './modes/*'],
presets: [ presets: ['@babel/preset-env', '@babel/preset-react', '@babel/preset-typescript'],
'@babel/preset-env',
'@babel/preset-react',
'@babel/preset-typescript',
],
plugins: [ plugins: [
[ [
'inline-react-svg', 'inline-react-svg',

View File

@ -54,11 +54,7 @@ const getCommandsModule = () => ({
const ExampleContext = React.createContext(); const ExampleContext = React.createContext();
function ExampleContextProvider({ children }) { function ExampleContextProvider({ children }) {
return ( return <ExampleContext.Provider value={{ example: 'value' }}>{children}</ExampleContext.Provider>;
<ExampleContext.Provider value={{ example: 'value' }}>
{children}
</ExampleContext.Provider>
);
} }
const getContextModule = () => [ const getContextModule = () => [

View File

@ -3,8 +3,7 @@ const { merge } = require('webpack-merge');
const path = require('path'); const path = require('path');
const webpackCommon = require('./../../../.webpack/webpack.base.js'); const webpackCommon = require('./../../../.webpack/webpack.base.js');
const pkg = require('./../package.json'); const pkg = require('./../package.json');
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer') const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
.BundleAnalyzerPlugin;
const ROOT_DIR = path.join(__dirname, './..'); const ROOT_DIR = path.join(__dirname, './..');
const SRC_DIR = path.join(__dirname, '../src'); const SRC_DIR = path.join(__dirname, '../src');
@ -38,13 +37,7 @@ module.exports = (env, argv) => {
libraryTarget: 'umd', libraryTarget: 'umd',
filename: pkg.main, filename: pkg.main,
}, },
externals: [ externals: [/\b(vtk.js)/, /\b(dcmjs)/, /\b(gl-matrix)/, /^@ohif/, /^@cornerstonejs/],
/\b(vtk.js)/,
/\b(dcmjs)/,
/\b(gl-matrix)/,
/^@ohif/,
/^@cornerstonejs/,
],
plugins: [ plugins: [
new webpack.optimize.LimitChunkCountPlugin({ new webpack.optimize.LimitChunkCountPlugin({
maxChunks: 1, maxChunks: 1,

View File

@ -10,7 +10,7 @@ module.exports = {
modules: 'commonjs', modules: 'commonjs',
debug: false, debug: false,
}, },
"@babel/preset-typescript", '@babel/preset-typescript',
], ],
'@babel/preset-react', '@babel/preset-react',
], ],
@ -26,7 +26,7 @@ module.exports = {
// WebPack handles ES6 --> Target Syntax // WebPack handles ES6 --> Target Syntax
['@babel/preset-env', { modules: false }], ['@babel/preset-env', { modules: false }],
'@babel/preset-react', '@babel/preset-react',
"@babel/preset-typescript", '@babel/preset-typescript',
], ],
ignore: ['**/*.test.jsx', '**/*.test.js', '__snapshots__', '__tests__'], ignore: ['**/*.test.jsx', '**/*.test.js', '__snapshots__', '__tests__'],
}, },
@ -35,7 +35,7 @@ module.exports = {
// WebPack handles ES6 --> Target Syntax // WebPack handles ES6 --> Target Syntax
['@babel/preset-env', { modules: false }], ['@babel/preset-env', { modules: false }],
'@babel/preset-react', '@babel/preset-react',
"@babel/preset-typescript", '@babel/preset-typescript',
], ],
plugins: ['react-hot-loader/babel'], plugins: ['react-hot-loader/babel'],
ignore: ['**/*.test.jsx', '**/*.test.js', '__snapshots__', '__tests__'], ignore: ['**/*.test.jsx', '**/*.test.js', '__snapshots__', '__tests__'],

View File

@ -7,11 +7,7 @@ const sopClassUids = ['1.2.840.10008.5.1.4.1.1.481.3'];
let loadPromises = {}; let loadPromises = {};
function _getDisplaySetsFromSeries( function _getDisplaySetsFromSeries(instances, servicesManager, extensionManager) {
instances,
servicesManager,
extensionManager
) {
const instance = instances[0]; const instance = instances[0];
const { const {
@ -56,10 +52,7 @@ function _getDisplaySetsFromSeries(
}; };
let referencedSeriesSequence = instance.ReferencedSeriesSequence; let referencedSeriesSequence = instance.ReferencedSeriesSequence;
if ( if (instance.ReferencedFrameOfReferenceSequence && !instance.ReferencedSeriesSequence) {
instance.ReferencedFrameOfReferenceSequence &&
!instance.ReferencedSeriesSequence
) {
instance.ReferencedSeriesSequence = _deriveReferencedSeriesSequenceFromFrameOfReferenceSequence( instance.ReferencedSeriesSequence = _deriveReferencedSeriesSequenceFromFrameOfReferenceSequence(
instance.ReferencedFrameOfReferenceSequence instance.ReferencedFrameOfReferenceSequence
); );
@ -72,8 +65,7 @@ function _getDisplaySetsFromSeries(
const referencedSeries = referencedSeriesSequence[0]; const referencedSeries = referencedSeriesSequence[0];
displaySet.referencedImages = displaySet.referencedImages = instance.ReferencedSeriesSequence.ReferencedInstanceSequence;
instance.ReferencedSeriesSequence.ReferencedInstanceSequence;
displaySet.referencedSeriesInstanceUID = referencedSeries.SeriesInstanceUID; displaySet.referencedSeriesInstanceUID = referencedSeries.SeriesInstanceUID;
displaySet.getReferenceDisplaySet = () => { displaySet.getReferenceDisplaySet = () => {
@ -88,14 +80,12 @@ function _getDisplaySetsFromSeries(
const referencedDisplaySet = referencedDisplaySets[0]; const referencedDisplaySet = referencedDisplaySets[0];
displaySet.referencedDisplaySetInstanceUID = displaySet.referencedDisplaySetInstanceUID = referencedDisplaySet.displaySetInstanceUID;
referencedDisplaySet.displaySetInstanceUID;
return referencedDisplaySet; return referencedDisplaySet;
}; };
displaySet.load = ({ headers }) => displaySet.load = ({ headers }) => _load(displaySet, servicesManager, extensionManager, headers);
_load(displaySet, servicesManager, extensionManager, headers);
return [displaySet]; return [displaySet];
} }
@ -194,11 +184,7 @@ function getSopClassHandlerModule({ servicesManager, extensionManager }) {
name: 'dicom-rt', name: 'dicom-rt',
sopClassUids, sopClassUids,
getDisplaySetsFromSeries: instances => { getDisplaySetsFromSeries: instances => {
return _getDisplaySetsFromSeries( return _getDisplaySetsFromSeries(instances, servicesManager, extensionManager);
instances,
servicesManager,
extensionManager
);
}, },
}, },
]; ];

View File

@ -5,9 +5,7 @@ import getSopClassHandlerModule from './getSopClassHandlerModule';
import hydrateRTDisplaySet from './utils/_hydrateRT'; import hydrateRTDisplaySet from './utils/_hydrateRT';
const Component = React.lazy(() => { const Component = React.lazy(() => {
return import( return import(/* webpackPrefetch: true */ './viewports/OHIFCornerstoneRTViewport');
/* webpackPrefetch: true */ './viewports/OHIFCornerstoneRTViewport'
);
}); });
const OHIFCornerstoneRTViewport = props => { const OHIFCornerstoneRTViewport = props => {

View File

@ -25,23 +25,13 @@ async function checkAndLoadContourData(instance, datasource) {
if (Array.isArray(contourData)) { if (Array.isArray(contourData)) {
promisesMap.has(referencedROINumber) promisesMap.has(referencedROINumber)
? promisesMap ? promisesMap.get(referencedROINumber).push(Promise.resolve(contourData))
.get(referencedROINumber) : promisesMap.set(referencedROINumber, [Promise.resolve(contourData)]);
.push(Promise.resolve(contourData))
: promisesMap.set(referencedROINumber, [
Promise.resolve(contourData),
]);
} else if (contourData && contourData.BulkDataURI) { } else if (contourData && contourData.BulkDataURI) {
const bulkDataURI = contourData.BulkDataURI; const bulkDataURI = contourData.BulkDataURI;
if ( if (!datasource || !datasource.retrieve || !datasource.retrieve.bulkDataURI) {
!datasource || return Promise.reject('Invalid datasource object or retrieve function');
!datasource.retrieve ||
!datasource.retrieve.bulkDataURI
) {
return Promise.reject(
'Invalid datasource object or retrieve function'
);
} }
const bulkDataPromise = datasource.retrieve.bulkDataURI({ const bulkDataPromise = datasource.retrieve.bulkDataURI({
@ -74,10 +64,7 @@ async function checkAndLoadContourData(instance, datasource) {
ROIContour.ContourSequence.forEach((Contour, index) => { ROIContour.ContourSequence.forEach((Contour, index) => {
const promise = resolvedPromises[index]; const promise = resolvedPromises[index];
if (promise.status === 'fulfilled') { if (promise.status === 'fulfilled') {
if ( if (Array.isArray(promise.value) && promise.value.every(Number.isFinite)) {
Array.isArray(promise.value) &&
promise.value.every(Number.isFinite)
) {
// If promise.value is already an array of numbers, use it directly // If promise.value is already an array of numbers, use it directly
Contour.ContourData = promise.value; Contour.ContourData = promise.value;
} else { } else {
@ -85,13 +72,8 @@ async function checkAndLoadContourData(instance, datasource) {
const uint8Array = new Uint8Array(promise.value); const uint8Array = new Uint8Array(promise.value);
const textDecoder = new TextDecoder(); const textDecoder = new TextDecoder();
const dataUint8Array = textDecoder.decode(uint8Array); const dataUint8Array = textDecoder.decode(uint8Array);
if ( if (typeof dataUint8Array === 'string' && dataUint8Array.includes('\\')) {
typeof dataUint8Array === 'string' && Contour.ContourData = dataUint8Array.split('\\').map(parseFloat);
dataUint8Array.includes('\\')
) {
Contour.ContourData = dataUint8Array
.split('\\')
.map(parseFloat);
} else { } else {
Contour.ContourData = []; Contour.ContourData = [];
} }
@ -120,9 +102,8 @@ export default async function loadRTStruct(
const { bulkDataURI } = dataSource.getConfig?.() || {}; const { bulkDataURI } = dataSource.getConfig?.() || {};
const { dicomLoaderService } = utilityModule.exports; const { dicomLoaderService } = utilityModule.exports;
const imageIdSopInstanceUidPairs = _getImageIdSopInstanceUidPairsForDisplaySet( const imageIdSopInstanceUidPairs =
referencedDisplaySet _getImageIdSopInstanceUidPairsForDisplaySet(referencedDisplaySet);
);
// Set here is loading is asynchronous. // Set here is loading is asynchronous.
// If this function throws its set back to false. // If this function throws its set back to false.
@ -137,20 +118,14 @@ export default async function loadRTStruct(
); );
const dicomData = DicomMessage.readFile(segArrayBuffer); const dicomData = DicomMessage.readFile(segArrayBuffer);
const rtStructDataset = DicomMetaDictionary.naturalizeDataset( const rtStructDataset = DicomMetaDictionary.naturalizeDataset(dicomData.dict);
dicomData.dict
);
rtStructDataset._meta = DicomMetaDictionary.namifyDataset(dicomData.meta); rtStructDataset._meta = DicomMetaDictionary.namifyDataset(dicomData.meta);
instance = rtStructDataset; instance = rtStructDataset;
} else { } else {
await checkAndLoadContourData(instance, dataSource); await checkAndLoadContourData(instance, dataSource);
} }
const { const { StructureSetROISequence, ROIContourSequence, RTROIObservationsSequence } = instance;
StructureSetROISequence,
ROIContourSequence,
RTROIObservationsSequence,
} = instance;
// Define our structure set entry and add it to the rtstruct module state. // Define our structure set entry and add it to the rtstruct module state.
const structureSet = { const structureSet = {
@ -174,12 +149,8 @@ export default async function loadRTStruct(
const contourPoints = []; const contourPoints = [];
for (let c = 0; c < ContourSequenceArray.length; c++) { for (let c = 0; c < ContourSequenceArray.length; c++) {
const { const { ContourImageSequence, ContourData, NumberOfContourPoints, ContourGeometricType } =
ContourImageSequence, ContourSequenceArray[c];
ContourData,
NumberOfContourPoints,
ContourGeometricType,
} = ContourSequenceArray[c];
let isSupported = false; let isSupported = false;
@ -229,9 +200,7 @@ const _getImageId = (imageIdSopInstanceUidPairs, sopInstanceUID) => {
imageIdSopInstanceUidPairsEntry.sopInstanceUID === sopInstanceUID imageIdSopInstanceUidPairsEntry.sopInstanceUID === sopInstanceUID
); );
return imageIdSopInstanceUidPairsEntry return imageIdSopInstanceUidPairsEntry ? imageIdSopInstanceUidPairsEntry.imageId : null;
? imageIdSopInstanceUidPairsEntry.imageId
: null;
}; };
function _getImageIdSopInstanceUidPairsForDisplaySet(referencedDisplaySet) { function _getImageIdSopInstanceUidPairsForDisplaySet(referencedDisplaySet) {
@ -252,8 +221,7 @@ function _setROIContourMetadata(
isSupported isSupported
) { ) {
const StructureSetROI = StructureSetROISequence.find( const StructureSetROI = StructureSetROISequence.find(
structureSetROI => structureSetROI => structureSetROI.ROINumber === ROIContour.ReferencedROINumber
structureSetROI.ROINumber === ROIContour.ReferencedROINumber
); );
const ROIContourData = { const ROIContourData = {
@ -293,23 +261,15 @@ function _setROIContourDataColor(ROIContour, ROIContourData) {
} }
} }
function _setROIContourRTROIObservations( function _setROIContourRTROIObservations(ROIContourData, RTROIObservationsSequence, ROINumber) {
ROIContourData,
RTROIObservationsSequence,
ROINumber
) {
const RTROIObservations = RTROIObservationsSequence.find( const RTROIObservations = RTROIObservationsSequence.find(
RTROIObservations => RTROIObservations.ReferencedROINumber === ROINumber RTROIObservations => RTROIObservations.ReferencedROINumber === ROINumber
); );
if (RTROIObservations) { if (RTROIObservations) {
// Deep copy so we don't keep the reference to the dcmjs dataset entry. // Deep copy so we don't keep the reference to the dcmjs dataset entry.
const { const { ObservationNumber, ROIObservationDescription, RTROIInterpretedType, ROIInterpreter } =
ObservationNumber, RTROIObservations;
ROIObservationDescription,
RTROIInterpretedType,
ROIInterpreter,
} = RTROIObservations;
ROIContourData.RTROIObservations = { ROIContourData.RTROIObservations = {
ObservationNumber, ObservationNumber,

View File

@ -1,13 +1,6 @@
async function _hydrateRTDisplaySet({ async function _hydrateRTDisplaySet({ rtDisplaySet, viewportId, servicesManager }) {
rtDisplaySet, const { segmentationService, hangingProtocolService, viewportGridService } =
viewportId, servicesManager.services;
servicesManager,
}) {
const {
segmentationService,
hangingProtocolService,
viewportGridService,
} = servicesManager.services;
const displaySetInstanceUID = rtDisplaySet.referencedDisplaySetInstanceUID; const displaySetInstanceUID = rtDisplaySet.referencedDisplaySetInstanceUID;

View File

@ -1,10 +1,5 @@
function createRTToolGroupAndAddTools( function createRTToolGroupAndAddTools(ToolGroupService, customizationService, toolGroupId) {
ToolGroupService, const { tools } = customizationService.get('cornerstone.overlayViewportTools') ?? {};
customizationService,
toolGroupId
) {
const { tools } =
customizationService.get('cornerstone.overlayViewportTools') ?? {};
return ToolGroupService.createToolGroupAndAddTools(toolGroupId, tools, {}); return ToolGroupService.createToolGroupAndAddTools(toolGroupId, tools, {});
} }

View File

@ -16,7 +16,7 @@ function promptHydrateRT({
}) { }) {
const { uiViewportDialogService } = servicesManager.services; const { uiViewportDialogService } = servicesManager.services;
return new Promise(async function(resolve, reject) { return new Promise(async function (resolve, reject) {
const promptResult = await _askHydrate(uiViewportDialogService, viewportId); const promptResult = await _askHydrate(uiViewportDialogService, viewportId);
if (promptResult === RESPONSE.HYDRATE_SEG) { if (promptResult === RESPONSE.HYDRATE_SEG) {
@ -37,7 +37,7 @@ function promptHydrateRT({
} }
function _askHydrate(uiViewportDialogService, viewportId) { function _askHydrate(uiViewportDialogService, viewportId) {
return new Promise(function(resolve, reject) { return new Promise(function (resolve, reject) {
const message = 'Do you want to open this Segmentation?'; const message = 'Do you want to open this Segmentation?';
const actions = [ const actions = [
{ {

View File

@ -1,11 +1,7 @@
import React, { useCallback, useEffect, useRef, useState } from 'react'; import React, { useCallback, useEffect, useRef, useState } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import OHIF, { utils } from '@ohif/core'; import OHIF, { utils } from '@ohif/core';
import { import { ViewportActionBar, useViewportGrid, LoadingIndicatorTotalPercent } from '@ohif/ui';
ViewportActionBar,
useViewportGrid,
LoadingIndicatorTotalPercent,
} from '@ohif/ui';
import _hydrateRTdisplaySet from '../utils/_hydrateRT'; import _hydrateRTdisplaySet from '../utils/_hydrateRT';
import promptHydrateRT from '../utils/promptHydrateRT'; import promptHydrateRT from '../utils/promptHydrateRT';
@ -71,9 +67,7 @@ function OHIFCornerstoneRTViewport(props) {
const { viewports, activeViewportId } = viewportGrid; const { viewports, activeViewportId } = viewportGrid;
const referencedDisplaySet = rtDisplaySet.getReferenceDisplaySet(); const referencedDisplaySet = rtDisplaySet.getReferenceDisplaySet();
const referencedDisplaySetMetadata = _getReferencedDisplaySetMetadata( const referencedDisplaySetMetadata = _getReferencedDisplaySetMetadata(referencedDisplaySet);
referencedDisplaySet
);
referencedDisplaySetRef.current = { referencedDisplaySetRef.current = {
displaySet: referencedDisplaySet, displaySet: referencedDisplaySet,
@ -106,9 +100,7 @@ function OHIFCornerstoneRTViewport(props) {
'@ohif/extension-cornerstone.viewportModule.cornerstone' '@ohif/extension-cornerstone.viewportModule.cornerstone'
); );
const { const { displaySet: referencedDisplaySet } = referencedDisplaySetRef.current;
displaySet: referencedDisplaySet,
} = referencedDisplaySetRef.current;
// Todo: jump to the center of the first segment // Todo: jump to the center of the first segment
return ( return (
@ -146,11 +138,7 @@ function OHIFCornerstoneRTViewport(props) {
newSelectedSegmentIndex = numberOfSegments - 1; newSelectedSegmentIndex = numberOfSegments - 1;
} }
segmentationService.jumpToSegmentCenter( segmentationService.jumpToSegmentCenter(segmentationId, newSelectedSegmentIndex, toolGroupId);
segmentationId,
newSelectedSegmentIndex,
toolGroupId
);
setSelectedSegment(newSelectedSegmentIndex); setSelectedSegment(newSelectedSegmentIndex);
}, },
[selectedSegment] [selectedSegment]
@ -177,18 +165,14 @@ function OHIFCornerstoneRTViewport(props) {
const { unsubscribe } = segmentationService.subscribe( const { unsubscribe } = segmentationService.subscribe(
segmentationService.EVENTS.SEGMENTATION_LOADING_COMPLETE, segmentationService.EVENTS.SEGMENTATION_LOADING_COMPLETE,
evt => { evt => {
if ( if (evt.rtDisplaySet.displaySetInstanceUID === rtDisplaySet.displaySetInstanceUID) {
evt.rtDisplaySet.displaySetInstanceUID ===
rtDisplaySet.displaySetInstanceUID
) {
setRtIsLoading(false); setRtIsLoading(false);
} }
if (evt.overlappingSegments) { if (evt.overlappingSegments) {
uiNotificationService.show({ uiNotificationService.show({
title: 'Overlapping Segments', title: 'Overlapping Segments',
message: message: 'Overlapping segments detected which is not currently supported',
'Overlapping segments detected which is not currently supported',
type: 'warning', type: 'warning',
}); });
} }
@ -224,9 +208,7 @@ function OHIFCornerstoneRTViewport(props) {
displaySetService.EVENTS.DISPLAY_SETS_REMOVED, displaySetService.EVENTS.DISPLAY_SETS_REMOVED,
({ displaySetInstanceUIDs }) => { ({ displaySetInstanceUIDs }) => {
const activeViewport = viewports.get(activeViewportId); const activeViewport = viewports.get(activeViewportId);
if ( if (displaySetInstanceUIDs.includes(activeViewport.displaySetInstanceUID)) {
displaySetInstanceUIDs.includes(activeViewport.displaySetInstanceUID)
) {
viewportGridService.setDisplaySetsForViewport({ viewportGridService.setDisplaySetsForViewport({
viewportId: activeViewportId, viewportId: activeViewportId,
displaySetInstanceUIDs: [], displaySetInstanceUIDs: [],
@ -247,19 +229,13 @@ function OHIFCornerstoneRTViewport(props) {
return; return;
} }
toolGroup = createRTToolGroupAndAddTools( toolGroup = createRTToolGroupAndAddTools(toolGroupService, customizationService, toolGroupId);
toolGroupService,
customizationService,
toolGroupId
);
setToolGroupCreated(true); setToolGroupCreated(true);
return () => { return () => {
// remove the segmentation representations if seg displayset changed // remove the segmentation representations if seg displayset changed
segmentationService.removeSegmentationRepresentationFromToolGroup( segmentationService.removeSegmentationRepresentationFromToolGroup(toolGroupId);
toolGroupId
);
toolGroupService.destroyToolGroup(toolGroupId); toolGroupService.destroyToolGroup(toolGroupId);
}; };
@ -270,9 +246,7 @@ function OHIFCornerstoneRTViewport(props) {
return () => { return () => {
// remove the segmentation representations if seg displayset changed // remove the segmentation representations if seg displayset changed
segmentationService.removeSegmentationRepresentationFromToolGroup( segmentationService.removeSegmentationRepresentationFromToolGroup(toolGroupId);
toolGroupId
);
referencedDisplaySetRef.current = null; referencedDisplaySetRef.current = null;
}; };
}, [rtDisplaySet]); }, [rtDisplaySet]);
@ -351,26 +325,22 @@ function OHIFCornerstoneRTViewport(props) {
currentSeries: SeriesNumber, currentSeries: SeriesNumber,
seriesDescription: `RT Viewport ${SeriesDescription}`, seriesDescription: `RT Viewport ${SeriesDescription}`,
patientInformation: { patientInformation: {
patientName: PatientName patientName: PatientName ? OHIF.utils.formatPN(PatientName.Alphabetic) : '',
? OHIF.utils.formatPN(PatientName.Alphabetic)
: '',
patientSex: PatientSex || '', patientSex: PatientSex || '',
patientAge: PatientAge || '', patientAge: PatientAge || '',
MRN: PatientID || '', MRN: PatientID || '',
thickness: SliceThickness ? `${SliceThickness.toFixed(2)}mm` : '', thickness: SliceThickness ? `${SliceThickness.toFixed(2)}mm` : '',
spacing: spacing:
SpacingBetweenSlices !== undefined SpacingBetweenSlices !== undefined ? `${SpacingBetweenSlices.toFixed(2)}mm` : '',
? `${SpacingBetweenSlices.toFixed(2)}mm`
: '',
scanner: ManufacturerModelName || '', scanner: ManufacturerModelName || '',
}, },
}} }}
/> />
<div className="relative flex flex-row w-full h-full overflow-hidden"> <div className="relative flex h-full w-full flex-row overflow-hidden">
{rtIsLoading && ( {rtIsLoading && (
<LoadingIndicatorTotalPercent <LoadingIndicatorTotalPercent
className="w-full h-full" className="h-full w-full"
totalNumbers={processingProgress.totalSegments} totalNumbers={processingProgress.totalSegments}
percentComplete={processingProgress.percentComplete} percentComplete={processingProgress.percentComplete}
loadingText="Loading RTSTRUCT..." loadingText="Loading RTSTRUCT..."

View File

@ -13,27 +13,28 @@ export default function _getStatusComponent({ isHydrated, onStatusClick }) {
case true: case true:
StatusIcon = () => <Icon name="status-alert" />; StatusIcon = () => <Icon name="status-alert" />;
ToolTipMessage = () => ( ToolTipMessage = () => <div>This Segmentation is loaded in the segmentation panel</div>;
<div>This Segmentation is loaded in the segmentation panel</div>
);
break; break;
case false: case false:
StatusIcon = () => ( StatusIcon = () => (
<Icon className="text-aqua-pale" name="status-untracked" /> <Icon
className="text-aqua-pale"
name="status-untracked"
/>
); );
ToolTipMessage = () => <div>Click LOAD to load RTSTRUCT.</div>; ToolTipMessage = () => <div>Click LOAD to load RTSTRUCT.</div>;
} }
const StatusArea = () => ( const StatusArea = () => (
<div className="flex h-6 leading-6 cursor-default text-sm text-white"> <div className="flex h-6 cursor-default text-sm leading-6 text-white">
<div className="min-w-[45px] flex items-center p-1 rounded-l-xl rounded-r bg-customgray-100"> <div className="bg-customgray-100 flex min-w-[45px] items-center rounded-l-xl rounded-r p-1">
<StatusIcon /> <StatusIcon />
<span className="ml-1">RTSTRUCT</span> <span className="ml-1">RTSTRUCT</span>
</div> </div>
{!isHydrated && ( {!isHydrated && (
<div <div
className="ml-1 px-1.5 rounded cursor-pointer hover:text-black bg-primary-main hover:bg-primary-light" className="bg-primary-main hover:bg-primary-light ml-1 cursor-pointer rounded px-1.5 hover:text-black"
// Using onMouseUp here because onClick is not working when the viewport is not active and is styled with pointer-events:none // Using onMouseUp here because onClick is not working when the viewport is not active and is styled with pointer-events:none
onMouseUp={onStatusClick} onMouseUp={onStatusClick}
> >
@ -46,7 +47,10 @@ export default function _getStatusComponent({ isHydrated, onStatusClick }) {
return ( return (
<> <>
{ToolTipMessage && ( {ToolTipMessage && (
<Tooltip content={<ToolTipMessage />} position="bottom-left"> <Tooltip
content={<ToolTipMessage />}
position="bottom-left"
>
<StatusArea /> <StatusArea />
</Tooltip> </Tooltip>
)} )}

View File

@ -40,13 +40,7 @@ module.exports = (env, argv) => {
libraryTarget: 'umd', libraryTarget: 'umd',
filename: pkg.main, filename: pkg.main,
}, },
externals: [ externals: [/\b(vtk.js)/, /\b(dcmjs)/, /\b(gl-matrix)/, /^@ohif/, /^@cornerstonejs/],
/\b(vtk.js)/,
/\b(dcmjs)/,
/\b(gl-matrix)/,
/^@ohif/,
/^@cornerstonejs/,
],
plugins: [ plugins: [
new webpack.optimize.LimitChunkCountPlugin({ new webpack.optimize.LimitChunkCountPlugin({
maxChunks: 1, maxChunks: 1,

View File

@ -10,7 +10,7 @@ module.exports = {
modules: 'commonjs', modules: 'commonjs',
debug: false, debug: false,
}, },
"@babel/preset-typescript", '@babel/preset-typescript',
], ],
'@babel/preset-react', '@babel/preset-react',
], ],
@ -26,7 +26,7 @@ module.exports = {
// WebPack handles ES6 --> Target Syntax // WebPack handles ES6 --> Target Syntax
['@babel/preset-env', { modules: false }], ['@babel/preset-env', { modules: false }],
'@babel/preset-react', '@babel/preset-react',
"@babel/preset-typescript", '@babel/preset-typescript',
], ],
ignore: ['**/*.test.jsx', '**/*.test.js', '__snapshots__', '__tests__'], ignore: ['**/*.test.jsx', '**/*.test.js', '__snapshots__', '__tests__'],
}, },
@ -35,7 +35,7 @@ module.exports = {
// WebPack handles ES6 --> Target Syntax // WebPack handles ES6 --> Target Syntax
['@babel/preset-env', { modules: false }], ['@babel/preset-env', { modules: false }],
'@babel/preset-react', '@babel/preset-react',
"@babel/preset-typescript", '@babel/preset-typescript',
], ],
plugins: ['react-hot-loader/babel'], plugins: ['react-hot-loader/babel'],
ignore: ['**/*.test.jsx', '**/*.test.js', '__snapshots__', '__tests__'], ignore: ['**/*.test.jsx', '**/*.test.js', '__snapshots__', '__tests__'],

View File

@ -1,10 +1,5 @@
import { utils } from '@ohif/core'; import { utils } from '@ohif/core';
import { import { metaData, cache, triggerEvent, eventTarget } from '@cornerstonejs/core';
metaData,
cache,
triggerEvent,
eventTarget,
} from '@cornerstonejs/core';
import { adaptersSEG, Enums } from '@cornerstonejs/adapters'; import { adaptersSEG, Enums } from '@cornerstonejs/adapters';
import { SOPClassHandlerId } from './id'; import { SOPClassHandlerId } from './id';
@ -14,11 +9,7 @@ const sopClassUids = ['1.2.840.10008.5.1.4.1.1.66.4'];
let loadPromises = {}; let loadPromises = {};
function _getDisplaySetsFromSeries( function _getDisplaySetsFromSeries(instances, servicesManager, extensionManager) {
instances,
servicesManager,
extensionManager
) {
const instance = instances[0]; const instance = instances[0];
const { const {
@ -71,8 +62,7 @@ function _getDisplaySetsFromSeries(
const referencedSeries = referencedSeriesSequence[0]; const referencedSeries = referencedSeriesSequence[0];
displaySet.referencedImages = displaySet.referencedImages = instance.ReferencedSeriesSequence.ReferencedInstanceSequence;
instance.ReferencedSeriesSequence.ReferencedInstanceSequence;
displaySet.referencedSeriesInstanceUID = referencedSeries.SeriesInstanceUID; displaySet.referencedSeriesInstanceUID = referencedSeries.SeriesInstanceUID;
displaySet.getReferenceDisplaySet = () => { displaySet.getReferenceDisplaySet = () => {
@ -87,8 +77,7 @@ function _getDisplaySetsFromSeries(
const referencedDisplaySet = referencedDisplaySets[0]; const referencedDisplaySet = referencedDisplaySets[0];
displaySet.referencedDisplaySetInstanceUID = displaySet.referencedDisplaySetInstanceUID = referencedDisplaySet.displaySetInstanceUID;
referencedDisplaySet.displaySetInstanceUID;
// Todo: this needs to be able to work with other reference volumes (other than streaming) such as nifti, etc. // Todo: this needs to be able to work with other reference volumes (other than streaming) such as nifti, etc.
displaySet.referencedVolumeURI = referencedDisplaySet.displaySetInstanceUID; displaySet.referencedVolumeURI = referencedDisplaySet.displaySetInstanceUID;
@ -121,10 +110,7 @@ function _load(segDisplaySet, servicesManager, extensionManager, headers) {
// We don't want to fire multiple loads, so we'll wait for the first to finish // We don't want to fire multiple loads, so we'll wait for the first to finish
// and also return the same promise to any other callers. // and also return the same promise to any other callers.
loadPromises[SOPInstanceUID] = new Promise(async (resolve, reject) => { loadPromises[SOPInstanceUID] = new Promise(async (resolve, reject) => {
if ( if (!segDisplaySet.segments || Object.keys(segDisplaySet.segments).length === 0) {
!segDisplaySet.segments ||
Object.keys(segDisplaySet.segments).length === 0
) {
await _loadSegments({ await _loadSegments({
extensionManager, extensionManager,
servicesManager, servicesManager,
@ -149,12 +135,7 @@ function _load(segDisplaySet, servicesManager, extensionManager, headers) {
return loadPromises[SOPInstanceUID]; return loadPromises[SOPInstanceUID];
} }
async function _loadSegments({ async function _loadSegments({ extensionManager, servicesManager, segDisplaySet, headers }) {
extensionManager,
servicesManager,
segDisplaySet,
headers,
}) {
const utilityModule = extensionManager.getModuleEntry( const utilityModule = extensionManager.getModuleEntry(
'@ohif/extension-cornerstone.utilityModule.common' '@ohif/extension-cornerstone.utilityModule.common'
); );
@ -162,15 +143,9 @@ async function _loadSegments({
const { segmentationService } = servicesManager.services; const { segmentationService } = servicesManager.services;
const { dicomLoaderService } = utilityModule.exports; const { dicomLoaderService } = utilityModule.exports;
const arrayBuffer = await dicomLoaderService.findDicomDataPromise( const arrayBuffer = await dicomLoaderService.findDicomDataPromise(segDisplaySet, null, headers);
segDisplaySet,
null,
headers
);
const cachedReferencedVolume = cache.getVolume( const cachedReferencedVolume = cache.getVolume(segDisplaySet.referencedVolumeId);
segDisplaySet.referencedVolumeId
);
if (!cachedReferencedVolume) { if (!cachedReferencedVolume) {
throw new Error( throw new Error(
@ -186,12 +161,9 @@ async function _loadSegments({
eventTarget.addEventListener(Enums.Events.SEGMENTATION_LOAD_PROGRESS, evt => { eventTarget.addEventListener(Enums.Events.SEGMENTATION_LOAD_PROGRESS, evt => {
const { percentComplete } = evt.detail; const { percentComplete } = evt.detail;
segmentationService._broadcastEvent( segmentationService._broadcastEvent(segmentationService.EVENTS.SEGMENT_LOADING_COMPLETE, {
segmentationService.EVENTS.SEGMENT_LOADING_COMPLETE,
{
percentComplete, percentComplete,
} });
);
}); });
const results = await adaptersSEG.Cornerstone3D.Segmentation.generateToolState( const results = await adaptersSEG.Cornerstone3D.Segmentation.generateToolState(
@ -212,18 +184,12 @@ async function _loadSegments({
function _segmentationExists(segDisplaySet, segmentationService) { function _segmentationExists(segDisplaySet, segmentationService) {
// This should be abstracted with the CornerstoneCacheService // This should be abstracted with the CornerstoneCacheService
return segmentationService.getSegmentation( return segmentationService.getSegmentation(segDisplaySet.displaySetInstanceUID);
segDisplaySet.displaySetInstanceUID
);
} }
function getSopClassHandlerModule({ servicesManager, extensionManager }) { function getSopClassHandlerModule({ servicesManager, extensionManager }) {
const getDisplaySetsFromSeries = instances => { const getDisplaySetsFromSeries = instances => {
return _getDisplaySetsFromSeries( return _getDisplaySetsFromSeries(instances, servicesManager, extensionManager);
instances,
servicesManager,
extensionManager
);
}; };
return [ return [

View File

@ -9,9 +9,7 @@ import getHangingProtocolModule from './getHangingProtocolModule';
import hydrateSEGDisplaySet from './utils/_hydrateSEG'; import hydrateSEGDisplaySet from './utils/_hydrateSEG';
const Component = React.lazy(() => { const Component = React.lazy(() => {
return import( return import(/* webpackPrefetch: true */ './viewports/OHIFCornerstoneSEGViewport');
/* webpackPrefetch: true */ './viewports/OHIFCornerstoneSEGViewport'
);
}); });
const OHIFCornerstoneSEGViewport = props => { const OHIFCornerstoneSEGViewport = props => {
@ -38,11 +36,7 @@ const extension = {
* iconName, iconLabel, label, component} object. Example of a panel module * iconName, iconLabel, label, component} object. Example of a panel module
* is the StudyBrowserPanel that is provided by the default extension in OHIF. * is the StudyBrowserPanel that is provided by the default extension in OHIF.
*/ */
getPanelModule: ({ getPanelModule: ({ servicesManager, commandsManager, extensionManager }): Types.Panel[] => {
servicesManager,
commandsManager,
extensionManager,
}): Types.Panel[] => {
const wrappedPanelSegmentation = () => { const wrappedPanelSegmentation = () => {
return ( return (
<PanelSegmentation <PanelSegmentation
@ -76,9 +70,7 @@ const extension = {
); );
}; };
return [ return [{ name: 'dicom-seg', component: ExtendedOHIFCornerstoneSEGViewport }];
{ name: 'dicom-seg', component: ExtendedOHIFCornerstoneSEGViewport },
];
}, },
/** /**
* SopClassHandlerModule should provide a list of sop class handlers that will be * SopClassHandlerModule should provide a list of sop class handlers that will be

View File

@ -5,10 +5,7 @@ import callInputDialog from './callInputDialog';
import { useAppConfig } from '@state'; import { useAppConfig } from '@state';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
export default function PanelSegmentation({ export default function PanelSegmentation({ servicesManager, commandsManager }) {
servicesManager,
commandsManager,
}) {
const { segmentationService, uiDialogService } = servicesManager.services; const { segmentationService, uiDialogService } = servicesManager.services;
const [appConfig] = useAppConfig(); const [appConfig] = useAppConfig();
const disableEditing = appConfig?.disableEditing; const disableEditing = appConfig?.disableEditing;
@ -19,9 +16,7 @@ export default function PanelSegmentation({
segmentationService.getConfiguration() segmentationService.getConfiguration()
); );
const [segmentations, setSegmentations] = useState(() => const [segmentations, setSegmentations] = useState(() => segmentationService.getSegmentations());
segmentationService.getSegmentations()
);
const [isMinimized, setIsMinimized] = useState({}); const [isMinimized, setIsMinimized] = useState({});
@ -78,32 +73,20 @@ export default function PanelSegmentation({
}; };
const getToolGroupIds = segmentationId => { const getToolGroupIds = segmentationId => {
const toolGroupIds = segmentationService.getToolGroupIdsWithSegmentation( const toolGroupIds = segmentationService.getToolGroupIdsWithSegmentation(segmentationId);
segmentationId
);
return toolGroupIds; return toolGroupIds;
}; };
const onSegmentClick = (segmentationId, segmentIndex) => { const onSegmentClick = (segmentationId, segmentIndex) => {
segmentationService.setActiveSegmentForSegmentation( segmentationService.setActiveSegmentForSegmentation(segmentationId, segmentIndex);
segmentationId,
segmentIndex
);
const toolGroupIds = getToolGroupIds(segmentationId); const toolGroupIds = getToolGroupIds(segmentationId);
toolGroupIds.forEach(toolGroupId => { toolGroupIds.forEach(toolGroupId => {
// const toolGroupId = // const toolGroupId =
segmentationService.setActiveSegmentationForToolGroup( segmentationService.setActiveSegmentationForToolGroup(segmentationId, toolGroupId);
segmentationId, segmentationService.jumpToSegmentCenter(segmentationId, segmentIndex, toolGroupId);
toolGroupId
);
segmentationService.jumpToSegmentCenter(
segmentationId,
segmentIndex,
toolGroupId
);
}); });
}; };
@ -118,11 +101,7 @@ export default function PanelSegmentation({
return; return;
} }
segmentationService.setSegmentLabelForSegmentation( segmentationService.setSegmentLabelForSegmentation(segmentationId, segmentIndex, label);
segmentationId,
segmentIndex,
label
);
}); });
}; };
@ -191,7 +170,7 @@ export default function PanelSegmentation({
); );
return ( return (
<div className="flex flex-col flex-auto min-h-0 justify-between mt-1"> <div className="mt-1 flex min-h-0 flex-auto flex-col justify-between">
{/* show segmentation table */} {/* show segmentation table */}
{segmentations?.length ? ( {segmentations?.length ? (
<SegmentationGroupTable <SegmentationGroupTable
@ -213,25 +192,13 @@ export default function PanelSegmentation({
onToggleMinimizeSegmentation={onToggleMinimizeSegmentation} onToggleMinimizeSegmentation={onToggleMinimizeSegmentation}
segmentationConfig={{ initialConfig: segmentationConfiguration }} segmentationConfig={{ initialConfig: segmentationConfiguration }}
setRenderOutline={value => setRenderOutline={value =>
_setSegmentationConfiguration( _setSegmentationConfiguration(selectedSegmentationId, 'renderOutline', value)
selectedSegmentationId,
'renderOutline',
value
)
} }
setOutlineOpacityActive={value => setOutlineOpacityActive={value =>
_setSegmentationConfiguration( _setSegmentationConfiguration(selectedSegmentationId, 'outlineOpacity', value)
selectedSegmentationId,
'outlineOpacity',
value
)
} }
setRenderFill={value => setRenderFill={value =>
_setSegmentationConfiguration( _setSegmentationConfiguration(selectedSegmentationId, 'renderFill', value)
selectedSegmentationId,
'renderFill',
value
)
} }
setRenderInactiveSegmentations={value => setRenderInactiveSegmentations={value =>
_setSegmentationConfiguration( _setSegmentationConfiguration(
@ -241,25 +208,13 @@ export default function PanelSegmentation({
) )
} }
setOutlineWidthActive={value => setOutlineWidthActive={value =>
_setSegmentationConfiguration( _setSegmentationConfiguration(selectedSegmentationId, 'outlineWidthActive', value)
selectedSegmentationId,
'outlineWidthActive',
value
)
} }
setFillAlpha={value => setFillAlpha={value =>
_setSegmentationConfiguration( _setSegmentationConfiguration(selectedSegmentationId, 'fillAlpha', value)
selectedSegmentationId,
'fillAlpha',
value
)
} }
setFillAlphaInactive={value => setFillAlphaInactive={value =>
_setSegmentationConfiguration( _setSegmentationConfiguration(selectedSegmentationId, 'fillAlphaInactive', value)
selectedSegmentationId,
'fillAlphaInactive',
value
)
} }
/> />
) : null} ) : null}

View File

@ -39,7 +39,7 @@ function callInputDialog(uiDialogService, label, callback) {
label="Enter the segment label" label="Enter the segment label"
labelClassName="text-white text-[14px] leading-[1.2]" labelClassName="text-white text-[14px] leading-[1.2]"
autoFocus autoFocus
className="bg-black border-primary-main" className="border-primary-main bg-black"
type="text" type="text"
value={value.label} value={value.label}
onChange={event => { onChange={event => {

View File

@ -3,11 +3,8 @@ async function _hydrateSEGDisplaySet({
viewportId: targetViewportId, viewportId: targetViewportId,
servicesManager, servicesManager,
}) { }) {
const { const { segmentationService, hangingProtocolService, viewportGridService } =
segmentationService, servicesManager.services;
hangingProtocolService,
viewportGridService,
} = servicesManager.services;
const displaySetInstanceUID = segDisplaySet.referencedDisplaySetInstanceUID; const displaySetInstanceUID = segDisplaySet.referencedDisplaySetInstanceUID;

View File

@ -6,9 +6,7 @@ import dcmjs from 'dcmjs';
* @returns The RGB color as an array of three integers between 0 and 255. * @returns The RGB color as an array of three integers between 0 and 255.
*/ */
function dicomlabToRGB(cielab: number[]): number[] { function dicomlabToRGB(cielab: number[]): number[] {
const rgb = dcmjs.data.Colors.dicomlab2RGB(cielab).map(x => const rgb = dcmjs.data.Colors.dicomlab2RGB(cielab).map(x => Math.round(x * 255));
Math.round(x * 255)
);
return rgb; return rgb;
} }

View File

@ -1,10 +1,5 @@
function createSEGToolGroupAndAddTools( function createSEGToolGroupAndAddTools(ToolGroupService, customizationService, toolGroupId) {
ToolGroupService, const { tools } = customizationService.get('cornerstone.overlayViewportTools') ?? {};
customizationService,
toolGroupId
) {
const { tools } =
customizationService.get('cornerstone.overlayViewportTools') ?? {};
return ToolGroupService.createToolGroupAndAddTools(toolGroupId, tools, {}); return ToolGroupService.createToolGroupAndAddTools(toolGroupId, tools, {});
} }

View File

@ -7,15 +7,10 @@ const RESPONSE = {
HYDRATE_SEG: 5, HYDRATE_SEG: 5,
}; };
function promptHydrateSEG({ function promptHydrateSEG({ servicesManager, segDisplaySet, viewportId, preHydrateCallbacks }) {
servicesManager,
segDisplaySet,
viewportId,
preHydrateCallbacks,
}) {
const { uiViewportDialogService } = servicesManager.services; const { uiViewportDialogService } = servicesManager.services;
return new Promise(async function(resolve, reject) { return new Promise(async function (resolve, reject) {
const promptResult = await _askHydrate(uiViewportDialogService, viewportId); const promptResult = await _askHydrate(uiViewportDialogService, viewportId);
if (promptResult === RESPONSE.HYDRATE_SEG) { if (promptResult === RESPONSE.HYDRATE_SEG) {
@ -35,7 +30,7 @@ function promptHydrateSEG({
} }
function _askHydrate(uiViewportDialogService, viewportId) { function _askHydrate(uiViewportDialogService, viewportId) {
return new Promise(function(resolve, reject) { return new Promise(function (resolve, reject) {
const message = 'Do you want to open this Segmentation?'; const message = 'Do you want to open this Segmentation?';
const actions = [ const actions = [
{ {

View File

@ -2,11 +2,7 @@ import PropTypes from 'prop-types';
import React, { useCallback, useEffect, useRef, useState } from 'react'; import React, { useCallback, useEffect, useRef, useState } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import OHIF, { utils } from '@ohif/core'; import OHIF, { utils } from '@ohif/core';
import { import { LoadingIndicatorTotalPercent, useViewportGrid, ViewportActionBar } from '@ohif/ui';
LoadingIndicatorTotalPercent,
useViewportGrid,
ViewportActionBar,
} from '@ohif/ui';
import createSEGToolGroupAndAddTools from '../utils/initSEGToolGroup'; import createSEGToolGroupAndAddTools from '../utils/initSEGToolGroup';
import promptHydrateSEG from '../utils/promptHydrateSEG'; import promptHydrateSEG from '../utils/promptHydrateSEG';
import hydrateSEGDisplaySet from '../utils/_hydrateSEG'; import hydrateSEGDisplaySet from '../utils/_hydrateSEG';
@ -106,9 +102,7 @@ function OHIFCornerstoneSEGViewport(props) {
'@ohif/extension-cornerstone.viewportModule.cornerstone' '@ohif/extension-cornerstone.viewportModule.cornerstone'
); );
const { const { displaySet: referencedDisplaySet } = referencedDisplaySetRef.current;
displaySet: referencedDisplaySet,
} = referencedDisplaySetRef.current;
// Todo: jump to the center of the first segment // Todo: jump to the center of the first segment
return ( return (
@ -148,11 +142,7 @@ function OHIFCornerstoneSEGViewport(props) {
newSelectedSegmentIndex = numberOfSegments - 1; newSelectedSegmentIndex = numberOfSegments - 1;
} }
segmentationService.jumpToSegmentCenter( segmentationService.jumpToSegmentCenter(segmentationId, newSelectedSegmentIndex, toolGroupId);
segmentationId,
newSelectedSegmentIndex,
toolGroupId
);
setSelectedSegment(newSelectedSegmentIndex); setSelectedSegment(newSelectedSegmentIndex);
}, },
[selectedSegment] [selectedSegment]
@ -179,18 +169,14 @@ function OHIFCornerstoneSEGViewport(props) {
const { unsubscribe } = segmentationService.subscribe( const { unsubscribe } = segmentationService.subscribe(
segmentationService.EVENTS.SEGMENTATION_LOADING_COMPLETE, segmentationService.EVENTS.SEGMENTATION_LOADING_COMPLETE,
evt => { evt => {
if ( if (evt.segDisplaySet.displaySetInstanceUID === segDisplaySet.displaySetInstanceUID) {
evt.segDisplaySet.displaySetInstanceUID ===
segDisplaySet.displaySetInstanceUID
) {
setSegIsLoading(false); setSegIsLoading(false);
} }
if (evt.overlappingSegments) { if (evt.overlappingSegments) {
uiNotificationService.show({ uiNotificationService.show({
title: 'Overlapping Segments', title: 'Overlapping Segments',
message: message: 'Overlapping segments detected which is not currently supported',
'Overlapping segments detected which is not currently supported',
type: 'warning', type: 'warning',
}); });
} }
@ -226,9 +212,7 @@ function OHIFCornerstoneSEGViewport(props) {
displaySetService.EVENTS.DISPLAY_SETS_REMOVED, displaySetService.EVENTS.DISPLAY_SETS_REMOVED,
({ displaySetInstanceUIDs }) => { ({ displaySetInstanceUIDs }) => {
const activeViewport = viewports.get(activeViewportId); const activeViewport = viewports.get(activeViewportId);
if ( if (displaySetInstanceUIDs.includes(activeViewport.displaySetInstanceUID)) {
displaySetInstanceUIDs.includes(activeViewport.displaySetInstanceUID)
) {
viewportGridService.setDisplaySetsForViewport({ viewportGridService.setDisplaySetsForViewport({
viewportId: activeViewportId, viewportId: activeViewportId,
displaySetInstanceUIDs: [], displaySetInstanceUIDs: [],
@ -251,17 +235,11 @@ function OHIFCornerstoneSEGViewport(props) {
// This creates a custom tool group which has the lifetime of this view // This creates a custom tool group which has the lifetime of this view
// only, and does NOT interfere with currently displayed segmentations. // only, and does NOT interfere with currently displayed segmentations.
toolGroup = createSEGToolGroupAndAddTools( toolGroup = createSEGToolGroupAndAddTools(toolGroupService, customizationService, toolGroupId);
toolGroupService,
customizationService,
toolGroupId
);
return () => { return () => {
// remove the segmentation representations if seg displayset changed // remove the segmentation representations if seg displayset changed
segmentationService.removeSegmentationRepresentationFromToolGroup( segmentationService.removeSegmentationRepresentationFromToolGroup(toolGroupId);
toolGroupId
);
// Only destroy the viewport specific implementation // Only destroy the viewport specific implementation
toolGroupService.destroyToolGroup(toolGroupId); toolGroupService.destroyToolGroup(toolGroupId);
@ -273,9 +251,7 @@ function OHIFCornerstoneSEGViewport(props) {
return () => { return () => {
// remove the segmentation representations if seg displayset changed // remove the segmentation representations if seg displayset changed
segmentationService.removeSegmentationRepresentationFromToolGroup( segmentationService.removeSegmentationRepresentationFromToolGroup(toolGroupId);
toolGroupId
);
referencedDisplaySetRef.current = null; referencedDisplaySetRef.current = null;
}; };
}, [segDisplaySet]); }, [segDisplaySet]);
@ -351,29 +327,23 @@ function OHIFCornerstoneSEGViewport(props) {
studyDate: formatDate(StudyDate), studyDate: formatDate(StudyDate),
seriesDescription: `SEG Viewport ${SeriesDescription}`, seriesDescription: `SEG Viewport ${SeriesDescription}`,
patientInformation: { patientInformation: {
patientName: PatientName patientName: PatientName ? OHIF.utils.formatPN(PatientName.Alphabetic) : '',
? OHIF.utils.formatPN(PatientName.Alphabetic)
: '',
patientSex: PatientSex || '', patientSex: PatientSex || '',
patientAge: PatientAge || '', patientAge: PatientAge || '',
MRN: PatientID || '', MRN: PatientID || '',
thickness: SliceThickness thickness: SliceThickness ? utils.roundNumber(SliceThickness, 2) : '',
? utils.roundNumber(SliceThickness, 2)
: '',
thicknessUnits: SliceThickness !== undefined ? 'mm' : '', thicknessUnits: SliceThickness !== undefined ? 'mm' : '',
spacing: spacing:
SpacingBetweenSlices !== undefined SpacingBetweenSlices !== undefined ? utils.roundNumber(SpacingBetweenSlices, 2) : '',
? utils.roundNumber(SpacingBetweenSlices, 2)
: '',
scanner: ManufacturerModelName || '', scanner: ManufacturerModelName || '',
}, },
}} }}
/> />
<div className="relative flex flex-row w-full h-full overflow-hidden"> <div className="relative flex h-full w-full flex-row overflow-hidden">
{segIsLoading && ( {segIsLoading && (
<LoadingIndicatorTotalPercent <LoadingIndicatorTotalPercent
className="w-full h-full" className="h-full w-full"
totalNumbers={processingProgress.totalSegments} totalNumbers={processingProgress.totalSegments}
percentComplete={processingProgress.percentComplete} percentComplete={processingProgress.percentComplete}
loadingText="Loading SEG..." loadingText="Loading SEG..."

View File

@ -13,27 +13,28 @@ export default function _getStatusComponent({ isHydrated, onStatusClick }) {
case true: case true:
StatusIcon = () => <Icon name="status-alert" />; StatusIcon = () => <Icon name="status-alert" />;
ToolTipMessage = () => ( ToolTipMessage = () => <div>This Segmentation is loaded in the segmentation panel</div>;
<div>This Segmentation is loaded in the segmentation panel</div>
);
break; break;
case false: case false:
StatusIcon = () => ( StatusIcon = () => (
<Icon className="text-aqua-pale" name="status-untracked" /> <Icon
className="text-aqua-pale"
name="status-untracked"
/>
); );
ToolTipMessage = () => <div>Click LOAD to load segmentation.</div>; ToolTipMessage = () => <div>Click LOAD to load segmentation.</div>;
} }
const StatusArea = () => ( const StatusArea = () => (
<div className="flex h-6 leading-6 cursor-default text-sm text-white"> <div className="flex h-6 cursor-default text-sm leading-6 text-white">
<div className="min-w-[45px] flex items-center p-1 rounded-l-xl rounded-r bg-customgray-100"> <div className="bg-customgray-100 flex min-w-[45px] items-center rounded-l-xl rounded-r p-1">
<StatusIcon /> <StatusIcon />
<span className="ml-1">SEG</span> <span className="ml-1">SEG</span>
</div> </div>
{!isHydrated && ( {!isHydrated && (
<div <div
className="ml-1 px-1.5 rounded cursor-pointer hover:text-black bg-primary-main hover:bg-primary-light" className="bg-primary-main hover:bg-primary-light ml-1 cursor-pointer rounded px-1.5 hover:text-black"
// Using onMouseUp here because onClick is not working when the viewport is not active and is styled with pointer-events:none // Using onMouseUp here because onClick is not working when the viewport is not active and is styled with pointer-events:none
onMouseUp={onStatusClick} onMouseUp={onStatusClick}
> >
@ -46,7 +47,10 @@ export default function _getStatusComponent({ isHydrated, onStatusClick }) {
return ( return (
<> <>
{ToolTipMessage && ( {ToolTipMessage && (
<Tooltip content={<ToolTipMessage />} position="bottom-left"> <Tooltip
content={<ToolTipMessage />}
position="bottom-left"
>
<StatusArea /> <StatusArea />
</Tooltip> </Tooltip>
)} )}

View File

@ -41,13 +41,7 @@ module.exports = (env, argv) => {
libraryTarget: 'umd', libraryTarget: 'umd',
filename: pkg.main, filename: pkg.main,
}, },
externals: [ externals: [/\b(vtk.js)/, /\b(dcmjs)/, /\b(gl-matrix)/, /^@ohif/, /^@cornerstonejs/],
/\b(vtk.js)/,
/\b(dcmjs)/,
/\b(gl-matrix)/,
/^@ohif/,
/^@cornerstonejs/,
],
plugins: [ plugins: [
new webpack.optimize.LimitChunkCountPlugin({ new webpack.optimize.LimitChunkCountPlugin({
maxChunks: 1, maxChunks: 1,

View File

@ -17,11 +17,7 @@ const { log } = OHIF;
* @param options Naturalized DICOM JSON headers to merge into the displaySet. * @param options Naturalized DICOM JSON headers to merge into the displaySet.
* *
*/ */
const _generateReport = ( const _generateReport = (measurementData, additionalFindingTypes, options = {}) => {
measurementData,
additionalFindingTypes,
options = {}
) => {
const filteredToolState = getFilteredCornerstoneToolState( const filteredToolState = getFilteredCornerstoneToolState(
measurementData, measurementData,
additionalFindingTypes additionalFindingTypes
@ -54,16 +50,8 @@ const commandsModule = ({}) => {
* as opposed to Finding Sites. * as opposed to Finding Sites.
* that you wish to serialize. * that you wish to serialize.
*/ */
downloadReport: ({ downloadReport: ({ measurementData, additionalFindingTypes, options = {} }) => {
measurementData, const srDataset = actions.generateReport(measurementData, additionalFindingTypes, options);
additionalFindingTypes,
options = {},
}) => {
const srDataset = actions.generateReport(
measurementData,
additionalFindingTypes,
options
);
const reportBlob = dcmjs.data.datasetToBlob(srDataset); const reportBlob = dcmjs.data.datasetToBlob(srDataset);
//Create a URL for the binary. //Create a URL for the binary.
@ -91,28 +79,19 @@ const commandsModule = ({}) => {
log.info('[DICOMSR] storeMeasurements'); log.info('[DICOMSR] storeMeasurements');
if (!dataSource || !dataSource.store || !dataSource.store.dicom) { if (!dataSource || !dataSource.store || !dataSource.store.dicom) {
log.error( log.error('[DICOMSR] datasource has no dataSource.store.dicom endpoint!');
'[DICOMSR] datasource has no dataSource.store.dicom endpoint!'
);
return Promise.reject({}); return Promise.reject({});
} }
try { try {
const naturalizedReport = _generateReport( const naturalizedReport = _generateReport(measurementData, additionalFindingTypes, options);
measurementData,
additionalFindingTypes,
options
);
const { StudyInstanceUID, ContentSequence } = naturalizedReport; const { StudyInstanceUID, ContentSequence } = naturalizedReport;
// The content sequence has 5 or more elements, of which // The content sequence has 5 or more elements, of which
// the `[4]` element contains the annotation data, so this is // the `[4]` element contains the annotation data, so this is
// checking that there is some annotation data present. // checking that there is some annotation data present.
if (!ContentSequence?.[4].ContentSequence?.length) { if (!ContentSequence?.[4].ContentSequence?.length) {
console.log( console.log('naturalizedReport missing imaging content', naturalizedReport);
'naturalizedReport missing imaging content',
naturalizedReport
);
throw new Error('Invalid report, no content'); throw new Error('Invalid report, no content');
} }
@ -130,12 +109,8 @@ const commandsModule = ({}) => {
return naturalizedReport; return naturalizedReport;
} catch (error) { } catch (error) {
console.warn(error); console.warn(error);
log.error( log.error(`[DICOMSR] Error while saving the measurements: ${error.message}`);
`[DICOMSR] Error while saving the measurements: ${error.message}` throw new Error(error.message || 'Error while saving the measurements.');
);
throw new Error(
error.message || 'Error while saving the measurements.'
);
} }
}, },
}; };

View File

@ -29,9 +29,7 @@ const validateSameStudyUID = (uid: string, instances): void => {
instances.forEach(it => { instances.forEach(it => {
if (it.StudyInstanceUID !== uid) { if (it.StudyInstanceUID !== uid) {
console.warn('Not all instances have the same UID', uid, it); console.warn('Not all instances have the same UID', uid, it);
throw new Error( throw new Error(`Instances ${it.SOPInstanceUID} does not belong to ${uid}`);
`Instances ${it.SOPInstanceUID} does not belong to ${uid}`
);
} }
}); });
}; };
@ -51,10 +49,7 @@ const CodeNameCodeSequenceValues = {
const CodingSchemeDesignators = { const CodingSchemeDesignators = {
SRT: 'SRT', SRT: 'SRT',
CornerstoneCodeSchemes: [ CornerstoneCodeSchemes: [Cornerstone3DCodeScheme.CodingSchemeDesignator, 'CST4'],
Cornerstone3DCodeScheme.CodingSchemeDesignator,
'CST4',
],
}; };
const RELATIONSHIP_TYPE = { const RELATIONSHIP_TYPE = {
@ -71,10 +66,7 @@ const CORNERSTONE_FREETEXT_CODE_VALUE = 'CORNERSTONEFREETEXT';
* @param instances is a list of instances from THIS series that are not * @param instances is a list of instances from THIS series that are not
* in this DICOM SR Display Set already. * in this DICOM SR Display Set already.
*/ */
function addInstances( function addInstances(instances: InstanceMetadata[], displaySetService: DisplaySetService) {
instances: InstanceMetadata[],
displaySetService: DisplaySetService
) {
this.instances.push(...instances); this.instances.push(...instances);
utils.sortStudyInstances(this.instances); utils.sortStudyInstances(this.instances);
// The last instance is the newest one, so is the one most interesting. // The last instance is the newest one, so is the one most interesting.
@ -93,11 +85,7 @@ function addInstances(
* @param servicesManager is the services that can be used for creating * @param servicesManager is the services that can be used for creating
* @returns The list of display sets created for the given instances object * @returns The list of display sets created for the given instances object
*/ */
function _getDisplaySetsFromSeries( function _getDisplaySetsFromSeries(instances, servicesManager, extensionManager) {
instances,
servicesManager,
extensionManager
) {
// If the series has no instances, stop here // If the series has no instances, stop here
if (!instances || !instances.length) { if (!instances || !instances.length) {
throw new Error('No instances were provided'); throw new Error('No instances were provided');
@ -123,8 +111,7 @@ function _getDisplaySetsFromSeries(
if ( if (
!ConceptNameCodeSequence || !ConceptNameCodeSequence ||
ConceptNameCodeSequence.CodeValue !== ConceptNameCodeSequence.CodeValue !== CodeNameCodeSequenceValues.ImagingMeasurementReport
CodeNameCodeSequenceValues.ImagingMeasurementReport
) { ) {
servicesManager.services.uiNotificationService.show({ servicesManager.services.uiNotificationService.show({
title: 'DICOM SR', title: 'DICOM SR',
@ -184,36 +171,21 @@ function _load(displaySet, servicesManager, extensionManager) {
// Check currently added displaySets and add measurements if the sources exist. // Check currently added displaySets and add measurements if the sources exist.
displaySetService.activeDisplaySets.forEach(activeDisplaySet => { displaySetService.activeDisplaySets.forEach(activeDisplaySet => {
_checkIfCanAddMeasurementsToDisplaySet( _checkIfCanAddMeasurementsToDisplaySet(displaySet, activeDisplaySet, dataSource);
displaySet,
activeDisplaySet,
dataSource
);
}); });
// Subscribe to new displaySets as the source may come in after. // Subscribe to new displaySets as the source may come in after.
displaySetService.subscribe( displaySetService.subscribe(displaySetService.EVENTS.DISPLAY_SETS_ADDED, data => {
displaySetService.EVENTS.DISPLAY_SETS_ADDED,
data => {
const { displaySetsAdded } = data; const { displaySetsAdded } = data;
// If there are still some measurements that have not yet been loaded into cornerstone, // If there are still some measurements that have not yet been loaded into cornerstone,
// See if we can load them onto any of the new displaySets. // See if we can load them onto any of the new displaySets.
displaySetsAdded.forEach(newDisplaySet => { displaySetsAdded.forEach(newDisplaySet => {
_checkIfCanAddMeasurementsToDisplaySet( _checkIfCanAddMeasurementsToDisplaySet(displaySet, newDisplaySet, dataSource);
displaySet, });
newDisplaySet,
dataSource
);
}); });
}
);
} }
function _checkIfCanAddMeasurementsToDisplaySet( function _checkIfCanAddMeasurementsToDisplaySet(srDisplaySet, newDisplaySet, dataSource) {
srDisplaySet,
newDisplaySet,
dataSource
) {
let unloadedMeasurements = srDisplaySet.measurements.filter( let unloadedMeasurements = srDisplaySet.measurements.filter(
measurement => measurement.loaded === false measurement => measurement.loaded === false
); );
@ -248,8 +220,7 @@ function _checkIfCanAddMeasurementsToDisplaySet(
const { coords } = measurement; const { coords } = measurement;
coords.forEach(coord => { coords.forEach(coord => {
const SOPInstanceUID = const SOPInstanceUID = coord.ReferencedSOPSequence.ReferencedSOPInstanceUID;
coord.ReferencedSOPSequence.ReferencedSOPInstanceUID;
if (!SOPInstanceUIDs.includes(SOPInstanceUID)) { if (!SOPInstanceUIDs.includes(SOPInstanceUID)) {
SOPInstanceUIDs.push(SOPInstanceUID); SOPInstanceUIDs.push(SOPInstanceUID);
@ -257,9 +228,7 @@ function _checkIfCanAddMeasurementsToDisplaySet(
}); });
}); });
const imageIdsForDisplaySet = dataSource.getImageIdsForDisplaySet( const imageIdsForDisplaySet = dataSource.getImageIdsForDisplaySet(newDisplaySet);
newDisplaySet
);
for (const imageId of imageIdsForDisplaySet) { for (const imageId of imageIdsForDisplaySet) {
if (!unloadedMeasurements.length) { if (!unloadedMeasurements.length) {
@ -267,25 +236,13 @@ function _checkIfCanAddMeasurementsToDisplaySet(
return; return;
} }
const { SOPInstanceUID, frameNumber } = metadataProvider.getUIDsFromImageID( const { SOPInstanceUID, frameNumber } = metadataProvider.getUIDsFromImageID(imageId);
imageId
);
if (SOPInstanceUIDs.includes(SOPInstanceUID)) { if (SOPInstanceUIDs.includes(SOPInstanceUID)) {
for (let j = unloadedMeasurements.length - 1; j >= 0; j--) { for (let j = unloadedMeasurements.length - 1; j >= 0; j--) {
const measurement = unloadedMeasurements[j]; const measurement = unloadedMeasurements[j];
if ( if (_measurementReferencesSOPInstanceUID(measurement, SOPInstanceUID, frameNumber)) {
_measurementReferencesSOPInstanceUID( addMeasurement(measurement, imageId, newDisplaySet.displaySetInstanceUID);
measurement,
SOPInstanceUID,
frameNumber
)
) {
addMeasurement(
measurement,
imageId,
newDisplaySet.displaySetInstanceUID
);
unloadedMeasurements.splice(j, 1); unloadedMeasurements.splice(j, 1);
} }
@ -294,11 +251,7 @@ function _checkIfCanAddMeasurementsToDisplaySet(
} }
} }
function _measurementReferencesSOPInstanceUID( function _measurementReferencesSOPInstanceUID(measurement, SOPInstanceUID, frameNumber) {
measurement,
SOPInstanceUID,
frameNumber
) {
const { coords } = measurement; const { coords } = measurement;
// NOTE: The ReferencedFrameNumber can be multiple values according to the DICOM // NOTE: The ReferencedFrameNumber can be multiple values according to the DICOM
@ -324,11 +277,7 @@ function _measurementReferencesSOPInstanceUID(
function getSopClassHandlerModule({ servicesManager, extensionManager }) { function getSopClassHandlerModule({ servicesManager, extensionManager }) {
const getDisplaySetsFromSeries = instances => { const getDisplaySetsFromSeries = instances => {
return _getDisplaySetsFromSeries( return _getDisplaySetsFromSeries(instances, servicesManager, extensionManager);
instances,
servicesManager,
extensionManager
);
}; };
return [ return [
@ -343,30 +292,22 @@ function getSopClassHandlerModule({ servicesManager, extensionManager }) {
function _getMeasurements(ImagingMeasurementReportContentSequence) { function _getMeasurements(ImagingMeasurementReportContentSequence) {
const ImagingMeasurements = ImagingMeasurementReportContentSequence.find( const ImagingMeasurements = ImagingMeasurementReportContentSequence.find(
item => item =>
item.ConceptNameCodeSequence.CodeValue === item.ConceptNameCodeSequence.CodeValue === CodeNameCodeSequenceValues.ImagingMeasurements
CodeNameCodeSequenceValues.ImagingMeasurements
); );
const MeasurementGroups = _getSequenceAsArray( const MeasurementGroups = _getSequenceAsArray(ImagingMeasurements.ContentSequence).filter(
ImagingMeasurements.ContentSequence item => item.ConceptNameCodeSequence.CodeValue === CodeNameCodeSequenceValues.MeasurementGroup
).filter(
item =>
item.ConceptNameCodeSequence.CodeValue ===
CodeNameCodeSequenceValues.MeasurementGroup
); );
const mergedContentSequencesByTrackingUniqueIdentifiers = _getMergedContentSequencesByTrackingUniqueIdentifiers( const mergedContentSequencesByTrackingUniqueIdentifiers =
MeasurementGroups _getMergedContentSequencesByTrackingUniqueIdentifiers(MeasurementGroups);
);
const measurements = []; const measurements = [];
Object.keys(mergedContentSequencesByTrackingUniqueIdentifiers).forEach( Object.keys(mergedContentSequencesByTrackingUniqueIdentifiers).forEach(
trackingUniqueIdentifier => { trackingUniqueIdentifier => {
const mergedContentSequence = const mergedContentSequence =
mergedContentSequencesByTrackingUniqueIdentifiers[ mergedContentSequencesByTrackingUniqueIdentifiers[trackingUniqueIdentifier];
trackingUniqueIdentifier
];
const measurement = _processMeasurement(mergedContentSequence); const measurement = _processMeasurement(mergedContentSequence);
@ -379,15 +320,11 @@ function _getMeasurements(ImagingMeasurementReportContentSequence) {
return measurements; return measurements;
} }
function _getMergedContentSequencesByTrackingUniqueIdentifiers( function _getMergedContentSequencesByTrackingUniqueIdentifiers(MeasurementGroups) {
MeasurementGroups
) {
const mergedContentSequencesByTrackingUniqueIdentifiers = {}; const mergedContentSequencesByTrackingUniqueIdentifiers = {};
MeasurementGroups.forEach(MeasurementGroup => { MeasurementGroups.forEach(MeasurementGroup => {
const ContentSequence = _getSequenceAsArray( const ContentSequence = _getSequenceAsArray(MeasurementGroup.ContentSequence);
MeasurementGroup.ContentSequence
);
const TrackingUniqueIdentifierItem = ContentSequence.find( const TrackingUniqueIdentifierItem = ContentSequence.find(
item => item =>
@ -396,22 +333,16 @@ function _getMergedContentSequencesByTrackingUniqueIdentifiers(
); );
if (!TrackingUniqueIdentifierItem) { if (!TrackingUniqueIdentifierItem) {
console.warn( console.warn('No Tracking Unique Identifier, skipping ambiguous measurement.');
'No Tracking Unique Identifier, skipping ambiguous measurement.'
);
} }
const trackingUniqueIdentifier = TrackingUniqueIdentifierItem.UID; const trackingUniqueIdentifier = TrackingUniqueIdentifierItem.UID;
if ( if (mergedContentSequencesByTrackingUniqueIdentifiers[trackingUniqueIdentifier] === undefined) {
mergedContentSequencesByTrackingUniqueIdentifiers[
trackingUniqueIdentifier
] === undefined
) {
// Add the full ContentSequence // Add the full ContentSequence
mergedContentSequencesByTrackingUniqueIdentifiers[ mergedContentSequencesByTrackingUniqueIdentifiers[trackingUniqueIdentifier] = [
trackingUniqueIdentifier ...ContentSequence,
] = [...ContentSequence]; ];
} else { } else {
// Add the ContentSequence minus the tracking identifier, as we have this // Add the ContentSequence minus the tracking identifier, as we have this
// Information in the merged ContentSequence anyway. // Information in the merged ContentSequence anyway.
@ -420,9 +351,7 @@ function _getMergedContentSequencesByTrackingUniqueIdentifiers(
item.ConceptNameCodeSequence.CodeValue !== item.ConceptNameCodeSequence.CodeValue !==
CodeNameCodeSequenceValues.TrackingUniqueIdentifier CodeNameCodeSequenceValues.TrackingUniqueIdentifier
) { ) {
mergedContentSequencesByTrackingUniqueIdentifiers[ mergedContentSequencesByTrackingUniqueIdentifiers[trackingUniqueIdentifier].push(item);
trackingUniqueIdentifier
].push(item);
} }
}); });
} }
@ -447,18 +376,12 @@ function _processTID1410Measurement(mergedContentSequence) {
// Need to deal with TID 1410 style measurements, which will have a SCOORD or SCOORD3D at the top level, // Need to deal with TID 1410 style measurements, which will have a SCOORD or SCOORD3D at the top level,
// And non-geometric representations where each NUM has "INFERRED FROM" SCOORD/SCOORD3D // And non-geometric representations where each NUM has "INFERRED FROM" SCOORD/SCOORD3D
const graphicItem = mergedContentSequence.find( const graphicItem = mergedContentSequence.find(group => group.ValueType === 'SCOORD');
group => group.ValueType === 'SCOORD'
);
const UIDREFContentItem = mergedContentSequence.find( const UIDREFContentItem = mergedContentSequence.find(group => group.ValueType === 'UIDREF');
group => group.ValueType === 'UIDREF'
);
const TrackingIdentifierContentItem = mergedContentSequence.find( const TrackingIdentifierContentItem = mergedContentSequence.find(
item => item => item.ConceptNameCodeSequence.CodeValue === CodeNameCodeSequenceValues.TrackingIdentifier
item.ConceptNameCodeSequence.CodeValue ===
CodeNameCodeSequenceValues.TrackingIdentifier
); );
if (!graphicItem) { if (!graphicItem) {
@ -468,9 +391,7 @@ function _processTID1410Measurement(mergedContentSequence) {
return; return;
} }
const NUMContentItems = mergedContentSequence.filter( const NUMContentItems = mergedContentSequence.filter(group => group.ValueType === 'NUM');
group => group.ValueType === 'NUM'
);
const measurement = { const measurement = {
loaded: false, loaded: false,
@ -485,10 +406,7 @@ function _processTID1410Measurement(mergedContentSequence) {
if (MeasuredValueSequence) { if (MeasuredValueSequence) {
measurement.labels.push( measurement.labels.push(
_getLabelFromMeasuredValueSequence( _getLabelFromMeasuredValueSequence(ConceptNameCodeSequence, MeasuredValueSequence)
ConceptNameCodeSequence,
MeasuredValueSequence
)
); );
} }
}); });
@ -497,32 +415,22 @@ function _processTID1410Measurement(mergedContentSequence) {
} }
function _processNonGeometricallyDefinedMeasurement(mergedContentSequence) { function _processNonGeometricallyDefinedMeasurement(mergedContentSequence) {
const NUMContentItems = mergedContentSequence.filter( const NUMContentItems = mergedContentSequence.filter(group => group.ValueType === 'NUM');
group => group.ValueType === 'NUM'
);
const UIDREFContentItem = mergedContentSequence.find( const UIDREFContentItem = mergedContentSequence.find(group => group.ValueType === 'UIDREF');
group => group.ValueType === 'UIDREF'
);
const TrackingIdentifierContentItem = mergedContentSequence.find( const TrackingIdentifierContentItem = mergedContentSequence.find(
item => item => item.ConceptNameCodeSequence.CodeValue === CodeNameCodeSequenceValues.TrackingIdentifier
item.ConceptNameCodeSequence.CodeValue ===
CodeNameCodeSequenceValues.TrackingIdentifier
); );
const finding = mergedContentSequence.find( const finding = mergedContentSequence.find(
item => item => item.ConceptNameCodeSequence.CodeValue === CodeNameCodeSequenceValues.Finding
item.ConceptNameCodeSequence.CodeValue ===
CodeNameCodeSequenceValues.Finding
); );
const findingSites = mergedContentSequence.filter( const findingSites = mergedContentSequence.filter(
item => item =>
item.ConceptNameCodeSequence.CodingSchemeDesignator === item.ConceptNameCodeSequence.CodingSchemeDesignator === CodingSchemeDesignators.SRT &&
CodingSchemeDesignators.SRT && item.ConceptNameCodeSequence.CodeValue === CodeNameCodeSequenceValues.FindingSite
item.ConceptNameCodeSequence.CodeValue ===
CodeNameCodeSequenceValues.FindingSite
); );
const measurement = { const measurement = {
@ -538,8 +446,7 @@ function _processNonGeometricallyDefinedMeasurement(mergedContentSequence) {
CodingSchemeDesignators.CornerstoneCodeSchemes.includes( CodingSchemeDesignators.CornerstoneCodeSchemes.includes(
finding.ConceptCodeSequence.CodingSchemeDesignator finding.ConceptCodeSequence.CodingSchemeDesignator
) && ) &&
finding.ConceptCodeSequence.CodeValue === finding.ConceptCodeSequence.CodeValue === CodeNameCodeSequenceValues.CornerstoneFreeText
CodeNameCodeSequenceValues.CornerstoneFreeText
) { ) {
measurement.labels.push({ measurement.labels.push({
label: CORNERSTONE_FREETEXT_CODE_VALUE, label: CORNERSTONE_FREETEXT_CODE_VALUE,
@ -554,8 +461,7 @@ function _processNonGeometricallyDefinedMeasurement(mergedContentSequence) {
CodingSchemeDesignators.CornerstoneCodeSchemes.includes( CodingSchemeDesignators.CornerstoneCodeSchemes.includes(
FindingSite.ConceptCodeSequence.CodingSchemeDesignator FindingSite.ConceptCodeSequence.CodingSchemeDesignator
) && ) &&
FindingSite.ConceptCodeSequence.CodeValue === FindingSite.ConceptCodeSequence.CodeValue === CodeNameCodeSequenceValues.CornerstoneFreeText
CodeNameCodeSequenceValues.CornerstoneFreeText
); );
if (cornerstoneFreeTextFindingSite) { if (cornerstoneFreeTextFindingSite) {
@ -567,18 +473,12 @@ function _processNonGeometricallyDefinedMeasurement(mergedContentSequence) {
} }
NUMContentItems.forEach(item => { NUMContentItems.forEach(item => {
const { const { ConceptNameCodeSequence, ContentSequence, MeasuredValueSequence } = item;
ConceptNameCodeSequence,
ContentSequence,
MeasuredValueSequence,
} = item;
const { ValueType } = ContentSequence; const { ValueType } = ContentSequence;
if (!ValueType === 'SCOORD') { if (!ValueType === 'SCOORD') {
console.warn( console.warn(`Graphic ${ValueType} not currently supported, skipping annotation.`);
`Graphic ${ValueType} not currently supported, skipping annotation.`
);
return; return;
} }
@ -591,10 +491,7 @@ function _processNonGeometricallyDefinedMeasurement(mergedContentSequence) {
if (MeasuredValueSequence) { if (MeasuredValueSequence) {
measurement.labels.push( measurement.labels.push(
_getLabelFromMeasuredValueSequence( _getLabelFromMeasuredValueSequence(ConceptNameCodeSequence, MeasuredValueSequence)
ConceptNameCodeSequence,
MeasuredValueSequence
)
); );
} }
}); });
@ -634,17 +531,12 @@ function _getCoordsFromSCOORDOrSCOORD3D(item) {
return coords; return coords;
} }
function _getLabelFromMeasuredValueSequence( function _getLabelFromMeasuredValueSequence(ConceptNameCodeSequence, MeasuredValueSequence) {
ConceptNameCodeSequence,
MeasuredValueSequence
) {
const { CodeMeaning } = ConceptNameCodeSequence; const { CodeMeaning } = ConceptNameCodeSequence;
const { NumericValue, MeasurementUnitsCodeSequence } = MeasuredValueSequence; const { NumericValue, MeasurementUnitsCodeSequence } = MeasuredValueSequence;
const { CodeValue } = MeasurementUnitsCodeSequence; const { CodeValue } = MeasurementUnitsCodeSequence;
const formatedNumericValue = NumericValue const formatedNumericValue = NumericValue ? Number(NumericValue).toFixed(2) : '';
? Number(NumericValue).toFixed(2)
: '';
return { return {
label: CodeMeaning, label: CodeMeaning,
@ -654,17 +546,11 @@ function _getLabelFromMeasuredValueSequence(
function _getReferencedImagesList(ImagingMeasurementReportContentSequence) { function _getReferencedImagesList(ImagingMeasurementReportContentSequence) {
const ImageLibrary = ImagingMeasurementReportContentSequence.find( const ImageLibrary = ImagingMeasurementReportContentSequence.find(
item => item => item.ConceptNameCodeSequence.CodeValue === CodeNameCodeSequenceValues.ImageLibrary
item.ConceptNameCodeSequence.CodeValue ===
CodeNameCodeSequenceValues.ImageLibrary
); );
const ImageLibraryGroup = _getSequenceAsArray( const ImageLibraryGroup = _getSequenceAsArray(ImageLibrary.ContentSequence).find(
ImageLibrary.ContentSequence item => item.ConceptNameCodeSequence.CodeValue === CodeNameCodeSequenceValues.ImageLibraryGroup
).find(
item =>
item.ConceptNameCodeSequence.CodeValue ===
CodeNameCodeSequenceValues.ImageLibraryGroup
); );
const referencedImages = []; const referencedImages = [];

View File

@ -1,8 +1,6 @@
import React from 'react'; import React from 'react';
import getSopClassHandlerModule from './getSopClassHandlerModule'; import getSopClassHandlerModule from './getSopClassHandlerModule';
import getHangingProtocolModule, { import getHangingProtocolModule, { srProtocol } from './getHangingProtocolModule';
srProtocol,
} from './getHangingProtocolModule';
import onModeEnter from './onModeEnter'; import onModeEnter from './onModeEnter';
import getCommandsModule from './commandsModule'; import getCommandsModule from './commandsModule';
import preRegistration from './init'; import preRegistration from './init';
@ -12,9 +10,7 @@ import hydrateStructuredReport from './utils/hydrateStructuredReport';
import createReferencedImageDisplaySet from './utils/createReferencedImageDisplaySet'; import createReferencedImageDisplaySet from './utils/createReferencedImageDisplaySet';
const Component = React.lazy(() => { const Component = React.lazy(() => {
return import( return import(/* webpackPrefetch: true */ './viewports/OHIFCornerstoneSRViewport');
/* webpackPrefetch: true */ './viewports/OHIFCornerstoneSRViewport'
);
}); });
const OHIFCornerstoneSRViewport = props => { const OHIFCornerstoneSRViewport = props => {

View File

@ -18,9 +18,7 @@ import toolNames from './tools/toolNames';
/** /**
* @param {object} configuration * @param {object} configuration
*/ */
export default function init({ export default function init({ configuration = {} }: Types.Extensions.ExtensionParams): void {
configuration = {},
}: Types.Extensions.ExtensionParams): void {
addTool(DICOMSRDisplayTool); addTool(DICOMSRDisplayTool);
addToolInstance(toolNames.SRLength, LengthTool, {}); addToolInstance(toolNames.SRLength, LengthTool, {});
addToolInstance(toolNames.SRBidirectional, BidirectionalTool); addToolInstance(toolNames.SRBidirectional, BidirectionalTool);

View File

@ -40,49 +40,32 @@ export default class DICOMSRDisplayTool extends AnnotationTool {
isPointNearTool = () => null; isPointNearTool = () => null;
getHandleNearImagePoint = () => null; getHandleNearImagePoint = () => null;
renderAnnotation = ( renderAnnotation = (enabledElement: Types.IEnabledElement, svgDrawingHelper: any): void => {
enabledElement: Types.IEnabledElement,
svgDrawingHelper: any
): void => {
const { viewport } = enabledElement; const { viewport } = enabledElement;
const { element } = viewport; const { element } = viewport;
let annotations = annotation.state.getAnnotations( let annotations = annotation.state.getAnnotations(this.getToolName(), element);
this.getToolName(),
element
);
// Todo: We don't need this anymore, filtering happens in triggerAnnotationRender // Todo: We don't need this anymore, filtering happens in triggerAnnotationRender
if (!annotations?.length) { if (!annotations?.length) {
return; return;
} }
annotations = this.filterInteractableAnnotationsForElement( annotations = this.filterInteractableAnnotationsForElement(element, annotations);
element,
annotations
);
if (!annotations?.length) { if (!annotations?.length) {
return; return;
} }
const trackingUniqueIdentifiersForElement = getTrackingUniqueIdentifiersForElement( const trackingUniqueIdentifiersForElement = getTrackingUniqueIdentifiersForElement(element);
element
);
const { const { activeIndex, trackingUniqueIdentifiers } = trackingUniqueIdentifiersForElement;
activeIndex,
trackingUniqueIdentifiers,
} = trackingUniqueIdentifiersForElement;
const activeTrackingUniqueIdentifier = const activeTrackingUniqueIdentifier = trackingUniqueIdentifiers[activeIndex];
trackingUniqueIdentifiers[activeIndex];
// Filter toolData to only render the data for the active SR. // Filter toolData to only render the data for the active SR.
const filteredAnnotations = annotations.filter(annotation => const filteredAnnotations = annotations.filter(annotation =>
trackingUniqueIdentifiers.includes( trackingUniqueIdentifiers.includes(annotation.data?.cachedStats?.TrackingUniqueIdentifier)
annotation.data?.cachedStats?.TrackingUniqueIdentifier
)
); );
if (!viewport._actors?.size) { if (!viewport._actors?.size) {
@ -138,8 +121,7 @@ export default class DICOMSRDisplayTool extends AnnotationTool {
break; break;
case SCOORD_TYPES.ELLIPSE: case SCOORD_TYPES.ELLIPSE:
renderMethod = this.renderEllipse; renderMethod = this.renderEllipse;
canvasCoordinatesAdapter = canvasCoordinatesAdapter = utilities.math.ellipse.getCanvasEllipseCorners;
utilities.math.ellipse.getCanvasEllipseCorners;
break; break;
default: default:
throw new Error(`Unsupported GraphicType: ${GraphicType}`); throw new Error(`Unsupported GraphicType: ${GraphicType}`);
@ -221,15 +203,9 @@ export default class DICOMSRDisplayTool extends AnnotationTool {
renderableData.map((data, index) => { renderableData.map((data, index) => {
canvasCoordinates = data.map(p => viewport.worldToCanvas(p)); canvasCoordinates = data.map(p => viewport.worldToCanvas(p));
const handleGroupUID = '0'; const handleGroupUID = '0';
drawing.drawHandles( drawing.drawHandles(svgDrawingHelper, annotationUID, handleGroupUID, canvasCoordinates, {
svgDrawingHelper,
annotationUID,
handleGroupUID,
canvasCoordinates,
{
color: options.color, color: options.color,
} });
);
}); });
} }
@ -248,10 +224,7 @@ export default class DICOMSRDisplayTool extends AnnotationTool {
canvasCoordinates.push(viewport.worldToCanvas(point)); canvasCoordinates.push(viewport.worldToCanvas(point));
// We get the other point for the arrow by using the image size // We get the other point for the arrow by using the image size
const imagePixelModule = metaData.get( const imagePixelModule = metaData.get('imagePixelModule', referencedImageId);
'imagePixelModule',
referencedImageId
);
let xOffset = 10; let xOffset = 10;
let yOffset = 10; let yOffset = 10;
@ -309,9 +282,7 @@ export default class DICOMSRDisplayTool extends AnnotationTool {
const rotation = viewport.getRotation(); const rotation = viewport.getRotation();
canvasCoordinates = ellipsePointsWorld.map(p => canvasCoordinates = ellipsePointsWorld.map(p => viewport.worldToCanvas(p));
viewport.worldToCanvas(p)
);
let canvasCorners; let canvasCorners;
if (rotation == 90 || rotation == 270) { if (rotation == 90 || rotation == 270) {
canvasCorners = utilities.math.ellipse.getCanvasEllipseCorners([ canvasCorners = utilities.math.ellipse.getCanvasEllipseCorners([
@ -366,23 +337,14 @@ export default class DICOMSRDisplayTool extends AnnotationTool {
adaptedCanvasCoordinates = canvasCoordinatesAdapter(canvasCoordinates); adaptedCanvasCoordinates = canvasCoordinatesAdapter(canvasCoordinates);
} }
const textLines = this._getTextBoxLinesFromLabels(label); const textLines = this._getTextBoxLinesFromLabels(label);
const canvasTextBoxCoords = utilities.drawing.getTextBoxCoordsCanvas( const canvasTextBoxCoords = utilities.drawing.getTextBoxCoordsCanvas(adaptedCanvasCoordinates);
adaptedCanvasCoordinates
);
annotation.data.handles.textBox.worldPosition = viewport.canvasToWorld( annotation.data.handles.textBox.worldPosition = viewport.canvasToWorld(canvasTextBoxCoords);
canvasTextBoxCoords
);
const textBoxPosition = viewport.worldToCanvas( const textBoxPosition = viewport.worldToCanvas(annotation.data.handles.textBox.worldPosition);
annotation.data.handles.textBox.worldPosition
);
const textBoxUID = '1'; const textBoxUID = '1';
const textBoxOptions = this.getLinkedTextBoxStyle( const textBoxOptions = this.getLinkedTextBoxStyle(styleSpecifier, annotation);
styleSpecifier,
annotation
);
const boundingBox = drawing.drawLinkedTextBox( const boundingBox = drawing.drawLinkedTextBox(
svgDrawingHelper, svgDrawingHelper,

View File

@ -27,15 +27,11 @@ function setTrackingUniqueIdentifiersForElement(
}; };
} }
function setActiveTrackingUniqueIdentifierForElement( function setActiveTrackingUniqueIdentifierForElement(element, TrackingUniqueIdentifier) {
element,
TrackingUniqueIdentifier
) {
const enabledElement = getEnabledElement(element); const enabledElement = getEnabledElement(element);
const { viewport } = enabledElement; const { viewport } = enabledElement;
const trackingIdentifiersForElement = const trackingIdentifiersForElement = state.trackingIdentifiersByViewportId[viewport.id];
state.trackingIdentifiersByViewportId[viewport.id];
if (trackingIdentifiersForElement) { if (trackingIdentifiersForElement) {
const activeIndex = trackingIdentifiersForElement.trackingUniqueIdentifiers.findIndex( const activeIndex = trackingIdentifiersForElement.trackingUniqueIdentifiers.findIndex(

View File

@ -8,11 +8,7 @@ const EPSILON = 1e-4;
const supportedLegacyCornerstoneTags = ['cornerstoneTools@^4.0.0']; const supportedLegacyCornerstoneTags = ['cornerstoneTools@^4.0.0'];
export default function addMeasurement( export default function addMeasurement(measurement, imageId, displaySetInstanceUID) {
measurement,
imageId,
displaySetInstanceUID
) {
// TODO -> Render rotated ellipse . // TODO -> Render rotated ellipse .
const toolName = toolNames.DICOMSRDisplay; const toolName = toolNames.DICOMSRDisplay;
@ -31,12 +27,7 @@ export default function addMeasurement(
} }
measurementData.renderableData[GraphicType].push( measurementData.renderableData[GraphicType].push(
_getRenderableData( _getRenderableData(GraphicType, GraphicData, imageId, measurement.TrackingIdentifier)
GraphicType,
GraphicData,
imageId,
measurement.TrackingIdentifier
)
); );
}); });
@ -86,12 +77,7 @@ export default function addMeasurement(
delete measurement.coords; delete measurement.coords;
} }
function _getRenderableData( function _getRenderableData(GraphicType, GraphicData, imageId, TrackingIdentifier) {
GraphicType,
GraphicData,
imageId,
TrackingIdentifier
) {
const [cornerstoneTag, toolName] = TrackingIdentifier.split(':'); const [cornerstoneTag, toolName] = TrackingIdentifier.split(':');
let renderableData: csTypes.Point3[]; let renderableData: csTypes.Point3[];
@ -207,38 +193,22 @@ function _getRenderableData(
throw new Error('imageId does not have imagePlaneModule metadata'); throw new Error('imageId does not have imagePlaneModule metadata');
} }
const { const { columnCosines }: { columnCosines: csTypes.Point3 } = imagePlaneModule;
columnCosines,
}: { columnCosines: csTypes.Point3 } = imagePlaneModule;
// find which axis is parallel to the columnCosines // find which axis is parallel to the columnCosines
const columnCosinesVec = vec3.fromValues(...columnCosines); const columnCosinesVec = vec3.fromValues(...columnCosines);
const projectedMajorAxisOnColVec = Math.abs( const projectedMajorAxisOnColVec = Math.abs(vec3.dot(columnCosinesVec, majorAxisVec));
vec3.dot(columnCosinesVec, majorAxisVec) const projectedMinorAxisOnColVec = Math.abs(vec3.dot(columnCosinesVec, minorAxisVec));
);
const projectedMinorAxisOnColVec = Math.abs(
vec3.dot(columnCosinesVec, minorAxisVec)
);
const absoluteOfMajorDotProduct = Math.abs(projectedMajorAxisOnColVec); const absoluteOfMajorDotProduct = Math.abs(projectedMajorAxisOnColVec);
const absoluteOfMinorDotProduct = Math.abs(projectedMinorAxisOnColVec); const absoluteOfMinorDotProduct = Math.abs(projectedMinorAxisOnColVec);
renderableData = []; renderableData = [];
if (Math.abs(absoluteOfMajorDotProduct - 1) < EPSILON) { if (Math.abs(absoluteOfMajorDotProduct - 1) < EPSILON) {
renderableData = [ renderableData = [pointsWorld[0], pointsWorld[1], pointsWorld[2], pointsWorld[3]];
pointsWorld[0],
pointsWorld[1],
pointsWorld[2],
pointsWorld[3],
];
} else if (Math.abs(absoluteOfMinorDotProduct - 1) < EPSILON) { } else if (Math.abs(absoluteOfMinorDotProduct - 1) < EPSILON) {
renderableData = [ renderableData = [pointsWorld[2], pointsWorld[3], pointsWorld[0], pointsWorld[1]];
pointsWorld[2],
pointsWorld[3],
pointsWorld[0],
pointsWorld[1],
];
} else { } else {
console.warn('OBLIQUE ELLIPSE NOT YET SUPPORTED'); console.warn('OBLIQUE ELLIPSE NOT YET SUPPORTED');
} }

View File

@ -1,10 +1,6 @@
import { addTool } from '@cornerstonejs/tools'; import { addTool } from '@cornerstonejs/tools';
export default function addToolInstance( export default function addToolInstance(name: string, toolClass, configuration?): void {
name: string,
toolClass,
configuration?
): void {
class InstanceClass extends toolClass { class InstanceClass extends toolClass {
static toolName = name; static toolName = name;
} }

View File

@ -3,13 +3,8 @@ import { DisplaySetService, classes } from '@ohif/core';
const ImageSet = classes.ImageSet; const ImageSet = classes.ImageSet;
const findInstance = (measurement, displaySetService: DisplaySetService) => { const findInstance = (measurement, displaySetService: DisplaySetService) => {
const { const { displaySetInstanceUID, ReferencedSOPInstanceUID: sopUid } = measurement;
displaySetInstanceUID, const referencedDisplaySet = displaySetService.getDisplaySetByUID(displaySetInstanceUID);
ReferencedSOPInstanceUID: sopUid,
} = measurement;
const referencedDisplaySet = displaySetService.getDisplaySetByUID(
displaySetInstanceUID
);
if (!referencedDisplaySet.images) { if (!referencedDisplaySet.images) {
return; return;
} }
@ -20,10 +15,7 @@ const findInstance = (measurement, displaySetService: DisplaySetService) => {
* contained within the provided display set. * contained within the provided display set.
* @return an array of instances referenced. * @return an array of instances referenced.
*/ */
const findReferencedInstances = ( const findReferencedInstances = (displaySetService: DisplaySetService, displaySet) => {
displaySetService: DisplaySetService,
displaySet
) => {
const instances = []; const instances = [];
const instanceById = {}; const instanceById = {};
for (const measurement of displaySet.measurements) { for (const measurement of displaySet.measurements) {
@ -58,7 +50,7 @@ const findReferencedInstances = (
const createReferencedImageDisplaySet = (displaySetService, displaySet) => { const createReferencedImageDisplaySet = (displaySetService, displaySet) => {
const instances = findReferencedInstances(displaySetService, displaySet); const instances = findReferencedInstances(displaySetService, displaySet);
// This will be a member function of the created image set // This will be a member function of the created image set
const updateInstances = function() { const updateInstances = function () {
this.images.splice( this.images.splice(
0, 0,
this.images.length, this.images.length,

View File

@ -14,8 +14,7 @@ const findInstanceMetadataBySopInstanceUID = (displaySets, SOPInstanceUID) => {
} }
instanceFound = displaySet.images.find( instanceFound = displaySet.images.find(
instanceMetadata => instanceMetadata => instanceMetadata.getSOPInstanceUID() === SOPInstanceUID
instanceMetadata.getSOPInstanceUID() === SOPInstanceUID
); );
return !!instanceFound; return !!instanceFound;

View File

@ -18,10 +18,7 @@ const findMostRecentStructuredReport = studies => {
} }
if (isStructuredReportSeries(series)) { if (isStructuredReportSeries(series)) {
if ( if (!mostRecentStructuredReport || compareSeriesDate(series, mostRecentStructuredReport)) {
!mostRecentStructuredReport ||
compareSeriesDate(series, mostRecentStructuredReport)
) {
mostRecentStructuredReport = series; mostRecentStructuredReport = series;
} }
} }
@ -38,10 +35,7 @@ const findMostRecentStructuredReport = studies => {
* @returns {boolean} * @returns {boolean}
*/ */
const isStructuredReportSeries = series => { const isStructuredReportSeries = series => {
const supportedSopClassUIDs = [ const supportedSopClassUIDs = ['1.2.840.10008.5.1.4.1.1.88.22', '1.2.840.10008.5.1.4.1.1.11.1'];
'1.2.840.10008.5.1.4.1.1.88.22',
'1.2.840.10008.5.1.4.1.1.11.1',
];
const firstInstance = series.getFirstInstance(); const firstInstance = series.getFirstInstance();
const SOPClassUID = firstInstance.getData().metadata.SOPClassUID; const SOPClassUID = firstInstance.getData().metadata.SOPClassUID;

View File

@ -2,17 +2,12 @@ import OHIF from '@ohif/core';
import { annotation } from '@cornerstonejs/tools'; import { annotation } from '@cornerstonejs/tools';
const { log } = OHIF; const { log } = OHIF;
function getFilteredCornerstoneToolState( function getFilteredCornerstoneToolState(measurementData, additionalFindingTypes) {
measurementData,
additionalFindingTypes
) {
const filteredToolState = {}; const filteredToolState = {};
function addToFilteredToolState(annotation, toolType) { function addToFilteredToolState(annotation, toolType) {
if (!annotation.metadata?.referencedImageId) { if (!annotation.metadata?.referencedImageId) {
log.warn( log.warn(`[DICOMSR] No referencedImageId found for ${toolType} ${annotation.id}`);
`[DICOMSR] No referencedImageId found for ${toolType} ${annotation.id}`
);
return; return;
} }
@ -30,9 +25,7 @@ function getFilteredCornerstoneToolState(
}; };
} }
const measurementDataI = measurementData.find( const measurementDataI = measurementData.find(md => md.uid === annotation.annotationUID);
md => md.uid === annotation.annotationUID
);
const toolData = imageIdSpecificToolState[toolType].data; const toolData = imageIdSpecificToolState[toolType].data;
let { finding } = measurementDataI; let { finding } = measurementDataI;
@ -77,9 +70,7 @@ function getFilteredCornerstoneToolState(
for (let i = 0; i < framesOfReference.length; i++) { for (let i = 0; i < framesOfReference.length; i++) {
const frameOfReference = framesOfReference[i]; const frameOfReference = framesOfReference[i];
const frameOfReferenceAnnotations = annotationManager.getAnnotations( const frameOfReferenceAnnotations = annotationManager.getAnnotations(frameOfReference);
frameOfReference
);
const toolTypes = Object.keys(frameOfReferenceAnnotations); const toolTypes = Object.keys(frameOfReferenceAnnotations);
@ -91,9 +82,7 @@ function getFilteredCornerstoneToolState(
if (annotations) { if (annotations) {
for (let k = 0; k < annotations.length; k++) { for (let k = 0; k < annotations.length; k++) {
const annotation = annotations[k]; const annotation = annotations[k];
const uidIndex = uids.findIndex( const uidIndex = uids.findIndex(uid => uid === annotation.annotationUID);
uid => uid === annotation.annotationUID
);
if (uidIndex !== -1) { if (uidIndex !== -1) {
addToFilteredToolState(annotation, toolType); addToFilteredToolState(annotation, toolType);

View File

@ -9,9 +9,7 @@
export default function getLabelFromDCMJSImportedToolData(toolData) { export default function getLabelFromDCMJSImportedToolData(toolData) {
const { findingSites = [], finding } = toolData; const { findingSites = [], finding } = toolData;
let freeTextLabel = findingSites.find( let freeTextLabel = findingSites.find(fs => fs.CodeValue === 'CORNERSTONEFREETEXT');
fs => fs.CodeValue === 'CORNERSTONEFREETEXT'
);
if (freeTextLabel) { if (freeTextLabel) {
return freeTextLabel.CodeMeaning; return freeTextLabel.CodeMeaning;

View File

@ -49,19 +49,10 @@ export default function hydrateStructuredReport(
const annotationManager = CsAnnotation.state.getAnnotationManager(); const annotationManager = CsAnnotation.state.getAnnotationManager();
const disableEditing = appConfig?.disableEditing; const disableEditing = appConfig?.disableEditing;
const dataSource = extensionManager.getActiveDataSource()[0]; const dataSource = extensionManager.getActiveDataSource()[0];
const { const { measurementService, displaySetService, customizationService } = servicesManager.services;
measurementService,
displaySetService,
customizationService,
} = servicesManager.services;
const codingValues = customizationService.getCustomization( const codingValues = customizationService.getCustomization('codingValues', {});
'codingValues', const displaySet = displaySetService.getDisplaySetByUID(displaySetInstanceUID);
{}
);
const displaySet = displaySetService.getDisplaySetByUID(
displaySetInstanceUID
);
// TODO -> We should define a strict versioning somewhere. // TODO -> We should define a strict versioning somewhere.
const mappings = measurementService.getSourceMappings( const mappings = measurementService.getSourceMappings(
@ -125,16 +116,14 @@ export default function hydrateStructuredReport(
// TODO: notification if no hydratable? // TODO: notification if no hydratable?
Object.keys(hydratableMeasurementsInSR).forEach(annotationType => { Object.keys(hydratableMeasurementsInSR).forEach(annotationType => {
const toolDataForAnnotationType = const toolDataForAnnotationType = hydratableMeasurementsInSR[annotationType];
hydratableMeasurementsInSR[annotationType];
toolDataForAnnotationType.forEach(toolData => { toolDataForAnnotationType.forEach(toolData => {
// Add the measurement to toolState // Add the measurement to toolState
// dcmjs and Cornerstone3D has structural defect in supporting multi-frame // dcmjs and Cornerstone3D has structural defect in supporting multi-frame
// files, and looking up the imageId from sopInstanceUIDToImageId results // files, and looking up the imageId from sopInstanceUIDToImageId results
// in the wrong value. // in the wrong value.
const frameNumber = const frameNumber = (toolData.annotation.data && toolData.annotation.data.frameNumber) || 1;
(toolData.annotation.data && toolData.annotation.data.frameNumber) || 1;
const imageId = const imageId =
imageIdsForToolState[toolData.sopInstanceUid][frameNumber] || imageIdsForToolState[toolData.sopInstanceUid][frameNumber] ||
sopInstanceUIDToImageId[toolData.sopInstanceUid]; sopInstanceUIDToImageId[toolData.sopInstanceUid];
@ -150,10 +139,7 @@ export default function hydrateStructuredReport(
for (let i = 0; i < imageIds.length; i++) { for (let i = 0; i < imageIds.length; i++) {
const imageId = imageIds[i]; const imageId = imageIds[i];
const { SeriesInstanceUID, StudyInstanceUID } = metaData.get( const { SeriesInstanceUID, StudyInstanceUID } = metaData.get('instance', imageId);
'instance',
imageId
);
if (!SeriesInstanceUIDs.includes(SeriesInstanceUID)) { if (!SeriesInstanceUIDs.includes(SeriesInstanceUID)) {
SeriesInstanceUIDs.push(SeriesInstanceUID); SeriesInstanceUIDs.push(SeriesInstanceUID);
@ -162,23 +148,19 @@ export default function hydrateStructuredReport(
if (!targetStudyInstanceUID) { if (!targetStudyInstanceUID) {
targetStudyInstanceUID = StudyInstanceUID; targetStudyInstanceUID = StudyInstanceUID;
} else if (targetStudyInstanceUID !== StudyInstanceUID) { } else if (targetStudyInstanceUID !== StudyInstanceUID) {
console.warn( console.warn('NO SUPPORT FOR SRs THAT HAVE MEASUREMENTS FROM MULTIPLE STUDIES.');
'NO SUPPORT FOR SRs THAT HAVE MEASUREMENTS FROM MULTIPLE STUDIES.'
);
} }
} }
Object.keys(hydratableMeasurementsInSR).forEach(annotationType => { Object.keys(hydratableMeasurementsInSR).forEach(annotationType => {
const toolDataForAnnotationType = const toolDataForAnnotationType = hydratableMeasurementsInSR[annotationType];
hydratableMeasurementsInSR[annotationType];
toolDataForAnnotationType.forEach(toolData => { toolDataForAnnotationType.forEach(toolData => {
// Add the measurement to toolState // Add the measurement to toolState
// dcmjs and Cornerstone3D has structural defect in supporting multi-frame // dcmjs and Cornerstone3D has structural defect in supporting multi-frame
// files, and looking up the imageId from sopInstanceUIDToImageId results // files, and looking up the imageId from sopInstanceUIDToImageId results
// in the wrong value. // in the wrong value.
const frameNumber = const frameNumber = (toolData.annotation.data && toolData.annotation.data.frameNumber) || 1;
(toolData.annotation.data && toolData.annotation.data.frameNumber) || 1;
const imageId = const imageId =
imageIdsForToolState[toolData.sopInstanceUid][frameNumber] || imageIdsForToolState[toolData.sopInstanceUid][frameNumber] ||
sopInstanceUIDToImageId[toolData.sopInstanceUid]; sopInstanceUIDToImageId[toolData.sopInstanceUid];
@ -208,19 +190,11 @@ export default function hydrateStructuredReport(
CORNERSTONE_3D_TOOLS_SOURCE_VERSION CORNERSTONE_3D_TOOLS_SOURCE_VERSION
); );
annotation.data.label = getLabelFromDCMJSImportedToolData(toolData); annotation.data.label = getLabelFromDCMJSImportedToolData(toolData);
annotation.data.finding = convertCode( annotation.data.finding = convertCode(codingValues, toolData.finding?.[0]);
codingValues, annotation.data.findingSites = convertSites(codingValues, toolData.findingSites);
toolData.finding?.[0]
);
annotation.data.findingSites = convertSites(
codingValues,
toolData.findingSites
);
annotation.data.site = annotation.data.findingSites?.[0]; annotation.data.site = annotation.data.findingSites?.[0];
const matchingMapping = mappings.find( const matchingMapping = mappings.find(m => m.annotationType === annotationType);
m => m.annotationType === annotationType
);
const newAnnotationUID = measurementService.addRawMeasurement( const newAnnotationUID = measurementService.addRawMeasurement(
source, source,
@ -231,9 +205,7 @@ export default function hydrateStructuredReport(
); );
if (disableEditing) { if (disableEditing) {
const addedAnnotation = annotationManager.getAnnotation( const addedAnnotation = annotationManager.getAnnotation(newAnnotationUID);
newAnnotationUID
);
locking.setAnnotationLocked(addedAnnotation, true); locking.setAnnotationLocked(addedAnnotation, true);
} }
@ -262,15 +234,14 @@ function _mapLegacyDataSet(dataset) {
); );
// Retrieve the Measurements themselves // Retrieve the Measurements themselves
const measurementGroups = toArray( const measurementGroups = toArray(imagingMeasurementContent.ContentSequence).filter(
imagingMeasurementContent.ContentSequence codeMeaningEquals(GROUP)
).filter(codeMeaningEquals(GROUP)); );
// For each of the supported measurement types, compute the measurement data // For each of the supported measurement types, compute the measurement data
const measurementData = {}; const measurementData = {};
const cornerstoneToolClasses = const cornerstoneToolClasses = MeasurementReport.CORNERSTONE_TOOL_CLASSES_BY_UTILITY_TYPE;
MeasurementReport.CORNERSTONE_TOOL_CLASSES_BY_UTILITY_TYPE;
const registeredToolClasses = []; const registeredToolClasses = [];
@ -280,13 +251,10 @@ function _mapLegacyDataSet(dataset) {
}); });
measurementGroups.forEach((measurementGroup, index) => { measurementGroups.forEach((measurementGroup, index) => {
const measurementGroupContentSequence = toArray( const measurementGroupContentSequence = toArray(measurementGroup.ContentSequence);
measurementGroup.ContentSequence
);
const TrackingIdentifierGroup = measurementGroupContentSequence.find( const TrackingIdentifierGroup = measurementGroupContentSequence.find(
contentItem => contentItem => contentItem.ConceptNameCodeSequence.CodeMeaning === TRACKING_IDENTIFIER
contentItem.ConceptNameCodeSequence.CodeMeaning === TRACKING_IDENTIFIER
); );
const TrackingIdentifier = TrackingIdentifierGroup.TextValue; const TrackingIdentifier = TrackingIdentifierGroup.TextValue;
@ -304,7 +272,7 @@ function _mapLegacyDataSet(dataset) {
return dataset; return dataset;
} }
const toArray = function(x) { const toArray = function (x) {
return Array.isArray(x) ? x : [x]; return Array.isArray(x) ? x : [x];
}; };

View File

@ -1,8 +1,7 @@
import { adaptersSR } from '@cornerstonejs/adapters'; import { adaptersSR } from '@cornerstonejs/adapters';
const cornerstoneAdapters = const cornerstoneAdapters =
adaptersSR.Cornerstone3D.MeasurementReport adaptersSR.Cornerstone3D.MeasurementReport.CORNERSTONE_TOOL_CLASSES_BY_UTILITY_TYPE;
.CORNERSTONE_TOOL_CLASSES_BY_UTILITY_TYPE;
const supportedLegacyCornerstoneTags = ['cornerstoneTools@^4.0.0']; const supportedLegacyCornerstoneTags = ['cornerstoneTools@^4.0.0'];
const CORNERSTONE_3D_TAG = cornerstoneAdapters.CORNERSTONE_3D_TAG; const CORNERSTONE_3D_TAG = cornerstoneAdapters.CORNERSTONE_3D_TAG;
@ -24,8 +23,7 @@ export default function isRehydratable(displaySet, mappings) {
const adapterKeys = Object.keys(cornerstoneAdapters).filter( const adapterKeys = Object.keys(cornerstoneAdapters).filter(
adapterKey => adapterKey =>
typeof cornerstoneAdapters[adapterKey] typeof cornerstoneAdapters[adapterKey].isValidCornerstoneTrackingIdentifier === 'function'
.isValidCornerstoneTrackingIdentifier === 'function'
); );
const adapters = []; const adapters = [];
@ -48,19 +46,13 @@ export default function isRehydratable(displaySet, mappings) {
const mappedTrackingIdentifier = `${cornerstoneTag}:${toolName}`; const mappedTrackingIdentifier = `${cornerstoneTag}:${toolName}`;
return adapter.isValidCornerstoneTrackingIdentifier( return adapter.isValidCornerstoneTrackingIdentifier(mappedTrackingIdentifier);
mappedTrackingIdentifier
);
}); });
if (hydratable) { if (hydratable) {
return true; return true;
} }
console.log( console.log('Measurement is not rehydratable', TrackingIdentifier, measurements[i]);
'Measurement is not rehydratable',
TrackingIdentifier,
measurements[i]
);
} }
console.log('No measurements found which were rehydratable'); console.log('No measurements found which were rehydratable');

View File

@ -11,8 +11,7 @@ import { useAppConfig } from '@state';
const { formatDate } = utils; const { formatDate } = utils;
const MEASUREMENT_TRACKING_EXTENSION_ID = const MEASUREMENT_TRACKING_EXTENSION_ID = '@ohif/extension-measurement-tracking';
'@ohif/extension-measurement-tracking';
const SR_TOOLGROUP_BASE_NAME = 'SRToolGroup'; const SR_TOOLGROUP_BASE_NAME = 'SRToolGroup';
@ -29,11 +28,8 @@ function OHIFCornerstoneSRViewport(props) {
const [appConfig] = useAppConfig(); const [appConfig] = useAppConfig();
const { const { displaySetService, cornerstoneViewportService, measurementService } =
displaySetService, servicesManager.services;
cornerstoneViewportService,
measurementService,
} = servicesManager.services;
const viewportId = viewportOptions.viewportId; const viewportId = viewportOptions.viewportId;
@ -47,13 +43,8 @@ function OHIFCornerstoneSRViewport(props) {
const [viewportGrid, viewportGridService] = useViewportGrid(); const [viewportGrid, viewportGridService] = useViewportGrid();
const [measurementSelected, setMeasurementSelected] = useState(0); const [measurementSelected, setMeasurementSelected] = useState(0);
const [measurementCount, setMeasurementCount] = useState(1); const [measurementCount, setMeasurementCount] = useState(1);
const [activeImageDisplaySetData, setActiveImageDisplaySetData] = useState( const [activeImageDisplaySetData, setActiveImageDisplaySetData] = useState(null);
null const [referencedDisplaySetMetadata, setReferencedDisplaySetMetadata] = useState(null);
);
const [
referencedDisplaySetMetadata,
setReferencedDisplaySetMetadata,
] = useState(null);
const [element, setElement] = useState(null); const [element, setElement] = useState(null);
const { viewports, activeViewportId } = viewportGrid; const { viewports, activeViewportId } = viewportGrid;
@ -83,9 +74,7 @@ function OHIFCornerstoneSRViewport(props) {
{ servicesManager, extensionManager, appConfig }, { servicesManager, extensionManager, appConfig },
displaySetInstanceUID displaySetInstanceUID
); );
const displaySets = displaySetService.getDisplaySetsForSeries( const displaySets = displaySetService.getDisplaySetsForSeries(SeriesInstanceUIDs[0]);
SeriesInstanceUIDs[0]
);
if (displaySets.length) { if (displaySets.length) {
viewportGridService.setDisplaySetsForViewports([ viewportGridService.setDisplaySetsForViewports([
{ {
@ -127,11 +116,7 @@ function OHIFCornerstoneSRViewport(props) {
const updateViewport = useCallback( const updateViewport = useCallback(
newMeasurementSelected => { newMeasurementSelected => {
const { const { StudyInstanceUID, displaySetInstanceUID, sopClassUids } = srDisplaySet;
StudyInstanceUID,
displaySetInstanceUID,
sopClassUids,
} = srDisplaySet;
if (!StudyInstanceUID || !displaySetInstanceUID) { if (!StudyInstanceUID || !displaySetInstanceUID) {
return; return;
@ -140,9 +125,7 @@ function OHIFCornerstoneSRViewport(props) {
if (sopClassUids && sopClassUids.length > 1) { if (sopClassUids && sopClassUids.length > 1) {
// Todo: what happens if there are multiple SOP Classes? Why we are // Todo: what happens if there are multiple SOP Classes? Why we are
// not throwing an error? // not throwing an error?
console.warn( console.warn('More than one SOPClassUID in the same series is not yet supported.');
'More than one SOPClassUID in the same series is not yet supported.'
);
} }
_getViewportReferencedDisplaySetData( _getViewportReferencedDisplaySetData(
@ -164,15 +147,11 @@ function OHIFCornerstoneSRViewport(props) {
// imageIdIndex will handle it by updating the viewport, but if they // imageIdIndex will handle it by updating the viewport, but if they
// are the same we just need to use measurementService to jump to the // are the same we just need to use measurementService to jump to the
// new measurement // new measurement
const csViewport = cornerstoneViewportService.getCornerstoneViewport( const csViewport = cornerstoneViewportService.getCornerstoneViewport(viewportId);
viewportId
);
const imageIds = csViewport.getImageIds(); const imageIds = csViewport.getImageIds();
const imageIdIndex = imageIds.indexOf( const imageIdIndex = imageIds.indexOf(measurements[newMeasurementSelected].imageId);
measurements[newMeasurementSelected].imageId
);
if (imageIdIndex !== -1) { if (imageIdIndex !== -1) {
csViewport.setImageIdIndex(imageIdIndex); csViewport.setImageIdIndex(imageIdIndex);
@ -251,12 +230,7 @@ function OHIFCornerstoneSRViewport(props) {
setTrackingIdentifiers(newMeasurementSelected); setTrackingIdentifiers(newMeasurementSelected);
updateViewport(newMeasurementSelected); updateViewport(newMeasurementSelected);
}, },
[ [measurementSelected, measurementCount, updateViewport, setTrackingIdentifiers]
measurementSelected,
measurementCount,
updateViewport,
setTrackingIdentifiers,
]
); );
/** /**
@ -267,9 +241,7 @@ function OHIFCornerstoneSRViewport(props) {
displaySetService.EVENTS.DISPLAY_SETS_REMOVED, displaySetService.EVENTS.DISPLAY_SETS_REMOVED,
({ displaySetInstanceUIDs }) => { ({ displaySetInstanceUIDs }) => {
const activeViewport = viewports[activeViewportId]; const activeViewport = viewports[activeViewportId];
if ( if (displaySetInstanceUIDs.includes(activeViewport.displaySetInstanceUID)) {
displaySetInstanceUIDs.includes(activeViewport.displaySetInstanceUID)
) {
viewportGridService.setDisplaySetsForViewport({ viewportGridService.setDisplaySetsForViewport({
viewportId: activeViewportId, viewportId: activeViewportId,
displaySetInstanceUIDs: [], displaySetInstanceUIDs: [],
@ -384,23 +356,19 @@ function OHIFCornerstoneSRViewport(props) {
currentSeries: SeriesNumber, currentSeries: SeriesNumber,
seriesDescription: SeriesDescription || '', seriesDescription: SeriesDescription || '',
patientInformation: { patientInformation: {
patientName: PatientName patientName: PatientName ? OHIF.utils.formatPN(PatientName.Alphabetic) : '',
? OHIF.utils.formatPN(PatientName.Alphabetic)
: '',
patientSex: PatientSex || '', patientSex: PatientSex || '',
patientAge: PatientAge || '', patientAge: PatientAge || '',
MRN: PatientID || '', MRN: PatientID || '',
thickness: SliceThickness ? `${SliceThickness.toFixed(2)}mm` : '', thickness: SliceThickness ? `${SliceThickness.toFixed(2)}mm` : '',
spacing: spacing:
SpacingBetweenSlices !== undefined SpacingBetweenSlices !== undefined ? `${SpacingBetweenSlices.toFixed(2)}mm` : '',
? `${SpacingBetweenSlices.toFixed(2)}mm`
: '',
scanner: ManufacturerModelName || '', scanner: ManufacturerModelName || '',
}, },
}} }}
/> />
<div className="relative flex flex-row w-full h-full overflow-hidden"> <div className="relative flex h-full w-full flex-row overflow-hidden">
{getCornerstoneViewport()} {getCornerstoneViewport()}
{childrenWithProps} {childrenWithProps}
</div> </div>
@ -435,9 +403,7 @@ async function _getViewportReferencedDisplaySetData(
const { displaySetInstanceUID } = measurement; const { displaySetInstanceUID } = measurement;
const referencedDisplaySet = displaySetService.getDisplaySetByUID( const referencedDisplaySet = displaySetService.getDisplaySetByUID(displaySetInstanceUID);
displaySetInstanceUID
);
const image0 = referencedDisplaySet.images[0]; const image0 = referencedDisplaySet.images[0];
const referencedDisplaySetMetadata = { const referencedDisplaySetMetadata = {
@ -477,8 +443,7 @@ function _getStatusComponent({
// 1 - Incompatible // 1 - Incompatible
// 2 - Locked // 2 - Locked
// 3 - Rehydratable / Open // 3 - Rehydratable / Open
const state = const state = isRehydratable && !isLocked ? 3 : isRehydratable && isLocked ? 2 : 1;
isRehydratable && !isLocked ? 3 : isRehydratable && isLocked ? 2 : 1;
let ToolTipMessage = null; let ToolTipMessage = null;
let StatusIcon = null; let StatusIcon = null;
@ -509,23 +474,24 @@ function _getStatusComponent({
break; break;
case 3: case 3:
StatusIcon = () => ( StatusIcon = () => (
<Icon className="text-aqua-pale" name="status-untracked" /> <Icon
className="text-aqua-pale"
name="status-untracked"
/>
); );
ToolTipMessage = () => ( ToolTipMessage = () => <div>{`Click ${loadStr} to restore measurements.`}</div>;
<div>{`Click ${loadStr} to restore measurements.`}</div>
);
} }
const StatusArea = () => ( const StatusArea = () => (
<div className="flex h-6 leading-6 cursor-default text-sm text-white"> <div className="flex h-6 cursor-default text-sm leading-6 text-white">
<div className="min-w-[45px] flex items-center p-1 rounded-l-xl rounded-r bg-customgray-100"> <div className="bg-customgray-100 flex min-w-[45px] items-center rounded-l-xl rounded-r p-1">
<StatusIcon /> <StatusIcon />
<span className="ml-1">SR</span> <span className="ml-1">SR</span>
</div> </div>
{state === 3 && ( {state === 3 && (
<div <div
className="ml-1 px-1.5 rounded cursor-pointer hover:text-black bg-primary-main hover:bg-primary-light" className="bg-primary-main hover:bg-primary-light ml-1 cursor-pointer rounded px-1.5 hover:text-black"
// Using onMouseUp here because onClick is not working when the viewport is not active and is styled with pointer-events:none // Using onMouseUp here because onClick is not working when the viewport is not active and is styled with pointer-events:none
onMouseUp={handleMouseUp} onMouseUp={handleMouseUp}
> >
@ -538,7 +504,10 @@ function _getStatusComponent({
return ( return (
<> <>
{ToolTipMessage && ( {ToolTipMessage && (
<Tooltip content={<ToolTipMessage />} position="bottom-left"> <Tooltip
content={<ToolTipMessage />}
position="bottom-left"
>
<StatusArea /> <StatusArea />
</Tooltip> </Tooltip>
)} )}

View File

@ -40,13 +40,7 @@ module.exports = (env, argv) => {
libraryTarget: 'umd', libraryTarget: 'umd',
filename: pkg.main, filename: pkg.main,
}, },
externals: [ externals: [/\b(vtk.js)/, /\b(dcmjs)/, /\b(gl-matrix)/, /^@ohif/, /^@cornerstonejs/],
/\b(vtk.js)/,
/\b(dcmjs)/,
/\b(gl-matrix)/,
/^@ohif/,
/^@cornerstonejs/,
],
plugins: [ plugins: [
new webpack.optimize.LimitChunkCountPlugin({ new webpack.optimize.LimitChunkCountPlugin({
maxChunks: 1, maxChunks: 1,

View File

@ -11,10 +11,7 @@ import {
} from '@cornerstonejs/core'; } from '@cornerstonejs/core';
import { MeasurementService } from '@ohif/core'; import { MeasurementService } from '@ohif/core';
import { Notification, useViewportDialog } from '@ohif/ui'; import { Notification, useViewportDialog } from '@ohif/ui';
import { import { IStackViewport, IVolumeViewport } from '@cornerstonejs/core/dist/esm/types';
IStackViewport,
IVolumeViewport,
} from '@cornerstonejs/core/dist/esm/types';
import { setEnabledElement } from '../state'; import { setEnabledElement } from '../state';
@ -42,24 +39,15 @@ function areEqual(prevProps, nextProps) {
return false; return false;
} }
if ( if (prevProps.viewportOptions.orientation !== nextProps.viewportOptions.orientation) {
prevProps.viewportOptions.orientation !==
nextProps.viewportOptions.orientation
) {
return false; return false;
} }
if ( if (prevProps.viewportOptions.toolGroupId !== nextProps.viewportOptions.toolGroupId) {
prevProps.viewportOptions.toolGroupId !==
nextProps.viewportOptions.toolGroupId
) {
return false; return false;
} }
if ( if (prevProps.viewportOptions.viewportType !== nextProps.viewportOptions.viewportType) {
prevProps.viewportOptions.viewportType !==
nextProps.viewportOptions.viewportType
) {
return false; return false;
} }
@ -79,8 +67,7 @@ function areEqual(prevProps, nextProps) {
const foundDisplaySet = nextDisplaySets.find( const foundDisplaySet = nextDisplaySets.find(
nextDisplaySet => nextDisplaySet =>
nextDisplaySet.displaySetInstanceUID === nextDisplaySet.displaySetInstanceUID === prevDisplaySet.displaySetInstanceUID
prevDisplaySet.displaySetInstanceUID
); );
if (!foundDisplaySet) { if (!foundDisplaySet) {
@ -95,9 +82,7 @@ function areEqual(prevProps, nextProps) {
// check if their imageIds are the same // check if their imageIds are the same
if (foundDisplaySet.images?.length) { if (foundDisplaySet.images?.length) {
for (let j = 0; j < foundDisplaySet.images.length; j++) { for (let j = 0; j < foundDisplaySet.images.length; j++) {
if ( if (foundDisplaySet.images[j].imageId !== prevDisplaySet.images[j].imageId) {
foundDisplaySet.images[j].imageId !== prevDisplaySet.images[j].imageId
) {
return false; return false;
} }
} }
@ -163,16 +148,9 @@ const OHIFCornerstoneViewport = React.memo(props => {
const renderingEngineId = viewportInfo.getRenderingEngineId(); const renderingEngineId = viewportInfo.getRenderingEngineId();
const syncGroups = viewportInfo.getSyncGroups(); const syncGroups = viewportInfo.getSyncGroups();
toolGroupService.removeViewportFromToolGroup( toolGroupService.removeViewportFromToolGroup(viewportId, renderingEngineId);
viewportId,
renderingEngineId
);
syncGroupService.removeViewportFromSyncGroup( syncGroupService.removeViewportFromSyncGroup(viewportId, renderingEngineId, syncGroups);
viewportId,
renderingEngineId,
syncGroups
);
}, },
[viewportId] [viewportId]
); );
@ -185,9 +163,7 @@ const OHIFCornerstoneViewport = React.memo(props => {
} }
const { viewportId, element } = evt.detail; const { viewportId, element } = evt.detail;
const viewportInfo = cornerstoneViewportService.getViewportInfo( const viewportInfo = cornerstoneViewportService.getViewportInfo(viewportId);
viewportId
);
setEnabledElement(viewportId, element); setEnabledElement(viewportId, element);
setEnabledVPElement(element); setEnabledVPElement(element);
@ -195,17 +171,9 @@ const OHIFCornerstoneViewport = React.memo(props => {
const toolGroupId = viewportInfo.getToolGroupId(); const toolGroupId = viewportInfo.getToolGroupId();
const syncGroups = viewportInfo.getSyncGroups(); const syncGroups = viewportInfo.getSyncGroups();
toolGroupService.addViewportToToolGroup( toolGroupService.addViewportToToolGroup(viewportId, renderingEngineId, toolGroupId);
viewportId,
renderingEngineId,
toolGroupId
);
syncGroupService.addViewportToSyncGroup( syncGroupService.addViewportToSyncGroup(viewportId, renderingEngineId, syncGroups);
viewportId,
renderingEngineId,
syncGroups
);
if (onElementEnabled) { if (onElementEnabled) {
onElementEnabled(evt); onElementEnabled(evt);
@ -218,17 +186,12 @@ const OHIFCornerstoneViewport = React.memo(props => {
useEffect(() => { useEffect(() => {
cornerstoneViewportService.enableViewport(viewportId, elementRef.current); cornerstoneViewportService.enableViewport(viewportId, elementRef.current);
eventTarget.addEventListener( eventTarget.addEventListener(Enums.Events.ELEMENT_ENABLED, elementEnabledHandler);
Enums.Events.ELEMENT_ENABLED,
elementEnabledHandler
);
setImageScrollBarHeight(); setImageScrollBarHeight();
return () => { return () => {
const viewportInfo = cornerstoneViewportService.getViewportInfo( const viewportInfo = cornerstoneViewportService.getViewportInfo(viewportId);
viewportId
);
if (!viewportInfo) { if (!viewportInfo) {
return; return;
@ -241,10 +204,7 @@ const OHIFCornerstoneViewport = React.memo(props => {
onElementDisabled(viewportInfo); onElementDisabled(viewportInfo);
} }
eventTarget.removeEventListener( eventTarget.removeEventListener(Enums.Events.ELEMENT_ENABLED, elementEnabledHandler);
Enums.Events.ELEMENT_ENABLED,
elementEnabledHandler
);
}; };
}, []); }, []);
@ -267,9 +227,7 @@ const OHIFCornerstoneViewport = React.memo(props => {
return; return;
} }
const viewportInfo = cornerstoneViewportService.getViewportInfo( const viewportInfo = cornerstoneViewportService.getViewportInfo(viewportId);
viewportId
);
if (viewportInfo.hasDisplaySet(invalidatedDisplaySetInstanceUID)) { if (viewportInfo.hasDisplaySet(invalidatedDisplaySetInstanceUID)) {
const viewportData = viewportInfo.getViewportData(); const viewportData = viewportInfo.getViewportData();
@ -281,11 +239,7 @@ const OHIFCornerstoneViewport = React.memo(props => {
); );
const keepCamera = true; const keepCamera = true;
cornerstoneViewportService.updateViewport( cornerstoneViewportService.updateViewport(viewportId, newViewportData, keepCamera);
viewportId,
newViewportData,
keepCamera
);
} }
} }
); );
@ -311,16 +265,11 @@ const OHIFCornerstoneViewport = React.memo(props => {
// The presentation state will have been stored previously by closing // The presentation state will have been stored previously by closing
// a viewport. Otherwise, this viewport will be unchanged and the // a viewport. Otherwise, this viewport will be unchanged and the
// presentation information will be directly carried over. // presentation information will be directly carried over.
const { const { lutPresentationStore, positionPresentationStore } = stateSyncService.getState();
lutPresentationStore,
positionPresentationStore,
} = stateSyncService.getState();
const { presentationIds } = viewportOptions; const { presentationIds } = viewportOptions;
const presentations = { const presentations = {
positionPresentation: positionPresentation: positionPresentationStore[presentationIds?.positionPresentationId],
positionPresentationStore[presentationIds?.positionPresentationId], lutPresentation: lutPresentationStore[presentationIds?.lutPresentationId],
lutPresentation:
lutPresentationStore[presentationIds?.lutPresentationId],
}; };
let measurement; let measurement;
if (cacheJumpToMeasurementEvent?.viewportId === viewportId) { if (cacheJumpToMeasurementEvent?.viewportId === viewportId) {
@ -459,7 +408,8 @@ function _subscribeToJumpToMeasurementEvents(
} }
if (cacheJumpToMeasurementEvent.cornerstoneViewport === undefined) { if (cacheJumpToMeasurementEvent.cornerstoneViewport === undefined) {
// Decide on which viewport should handle this // Decide on which viewport should handle this
cacheJumpToMeasurementEvent.cornerstoneViewport = cornerstoneViewportService.getViewportIdToJump( cacheJumpToMeasurementEvent.cornerstoneViewport =
cornerstoneViewportService.getViewportIdToJump(
jumpId, jumpId,
measurement.displaySetInstanceUID, measurement.displaySetInstanceUID,
{ referencedImageId: measurement.referencedImageId } { referencedImageId: measurement.referencedImageId }
@ -500,9 +450,7 @@ function _checkForCachedJumpToMeasurementEvents(
cacheJumpToMeasurementEvent = null; cacheJumpToMeasurementEvent = null;
return; return;
} }
const displaysUIDs = displaySets.map( const displaysUIDs = displaySets.map(displaySet => displaySet.displaySetInstanceUID);
displaySet => displaySet.displaySetInstanceUID
);
if (!displaysUIDs?.length) { if (!displaysUIDs?.length) {
return; return;
} }
@ -541,9 +489,7 @@ function _jumpToMeasurement(
return; return;
} }
const referencedDisplaySet = displaySetService.getDisplaySetByUID( const referencedDisplaySet = displaySetService.getDisplaySetByUID(displaySetInstanceUID);
displaySetInstanceUID
);
// Todo: setCornerstoneMeasurementActive should be handled by the toolGroupManager // Todo: setCornerstoneMeasurementActive should be handled by the toolGroupManager
// to set it properly // to set it properly
@ -555,9 +501,7 @@ function _jumpToMeasurement(
if (enabledElement) { if (enabledElement) {
// See how the jumpToSlice() of Cornerstone3D deals with imageIdx param. // See how the jumpToSlice() of Cornerstone3D deals with imageIdx param.
const viewport = enabledElement.viewport as const viewport = enabledElement.viewport as IStackViewport | IVolumeViewport;
| IStackViewport
| IVolumeViewport;
let imageIdIndex = 0; let imageIdIndex = 0;
let viewportCameraDirectionMatch = true; let viewportCameraDirectionMatch = true;
@ -565,14 +509,9 @@ function _jumpToMeasurement(
if (viewport instanceof StackViewport) { if (viewport instanceof StackViewport) {
const imageIds = viewport.getImageIds(); const imageIds = viewport.getImageIds();
imageIdIndex = imageIds.findIndex(imageId => { imageIdIndex = imageIds.findIndex(imageId => {
const { const { SOPInstanceUID: aSOPInstanceUID, frameNumber: aFrameNumber } =
SOPInstanceUID: aSOPInstanceUID, getSOPInstanceAttributes(imageId);
frameNumber: aFrameNumber, return aSOPInstanceUID === SOPInstanceUID && (!frameNumber || frameNumber === aFrameNumber);
} = getSOPInstanceAttributes(imageId);
return (
aSOPInstanceUID === SOPInstanceUID &&
(!frameNumber || frameNumber === aFrameNumber)
);
}); });
} else { } else {
// for volume viewport we can't rely on the imageIdIndex since it can be // for volume viewport we can't rely on the imageIdIndex since it can be
@ -587,10 +526,7 @@ function _jumpToMeasurement(
// should compare abs for both planes since the direction can be flipped // should compare abs for both planes since the direction can be flipped
if ( if (
measurementViewPlane && measurementViewPlane &&
!csUtils.isEqual( !csUtils.isEqual(measurementViewPlane.map(Math.abs), viewportViewPlane.map(Math.abs))
measurementViewPlane.map(Math.abs),
viewportViewPlane.map(Math.abs)
)
) { ) {
viewportCameraDirectionMatch = false; viewportCameraDirectionMatch = false;
} }
@ -629,10 +565,7 @@ OHIFCornerstoneViewport.propTypes = {
// Note: you SHOULD NOT use the initialImageIdOrIndex for manipulation // Note: you SHOULD NOT use the initialImageIdOrIndex for manipulation
// of the imageData in the OHIFCornerstoneViewport. This prop is used // of the imageData in the OHIFCornerstoneViewport. This prop is used
// to set the initial state of the viewport's first image to render // to set the initial state of the viewport's first image to render
initialImageIdOrIndex: PropTypes.oneOfType([ initialImageIdOrIndex: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
PropTypes.string,
PropTypes.number,
]),
}; };
export default OHIFCornerstoneViewport; export default OHIFCornerstoneViewport;

View File

@ -3,12 +3,7 @@ import { vec3 } from 'gl-matrix';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { metaData, Enums, utilities } from '@cornerstonejs/core'; import { metaData, Enums, utilities } from '@cornerstonejs/core';
import { ViewportOverlay } from '@ohif/ui'; import { ViewportOverlay } from '@ohif/ui';
import { import { formatPN, formatDICOMDate, formatDICOMTime, formatNumberPrecision } from './utils';
formatPN,
formatDICOMDate,
formatDICOMTime,
formatNumberPrecision,
} from './utils';
import { InstanceMetadata } from 'platform/core/src/types'; import { InstanceMetadata } from 'platform/core/src/types';
import { ServicesManager } from '@ohif/core'; import { ServicesManager } from '@ohif/core';
import { ImageSliceData } from '@cornerstonejs/core/dist/esm/types'; import { ImageSliceData } from '@cornerstonejs/core/dist/esm/types';
@ -55,13 +50,9 @@ function VOIOverlayItem({ voi, customization }: OverlayItemProps) {
style={{ color: (customization && customization.color) || undefined }} style={{ color: (customization && customization.color) || undefined }}
> >
<span className="mr-1 shrink-0">W:</span> <span className="mr-1 shrink-0">W:</span>
<span className="ml-1 mr-2 font-light shrink-0"> <span className="ml-1 mr-2 shrink-0 font-light">{windowWidth.toFixed(0)}</span>
{windowWidth.toFixed(0)}
</span>
<span className="mr-1 shrink-0">L:</span> <span className="mr-1 shrink-0">L:</span>
<span className="ml-1 font-light shrink-0"> <span className="ml-1 shrink-0 font-light">{windowCenter.toFixed(0)}</span>
{windowCenter.toFixed(0)}
</span>
</div> </div>
); );
} }
@ -116,11 +107,8 @@ function CustomizableViewportOverlay({
viewportId, viewportId,
servicesManager, servicesManager,
}) { }) {
const { const { toolbarService, cornerstoneViewportService, customizationService } =
toolbarService, servicesManager.services;
cornerstoneViewportService,
customizationService,
} = servicesManager.services;
const [voi, setVOI] = useState({ windowCenter: null, windowWidth: null }); const [voi, setVOI] = useState({ windowCenter: null, windowWidth: null });
const [scale, setScale] = useState(1); const [scale, setScale] = useState(1);
const [activeTools, setActiveTools] = useState([]); const [activeTools, setActiveTools] = useState([]);
@ -149,12 +137,7 @@ function CustomizableViewportOverlay({
const instanceNumber = useMemo(() => { const instanceNumber = useMemo(() => {
if (viewportData != null) { if (viewportData != null) {
return _getInstanceNumber( return _getInstanceNumber(viewportData, viewportId, imageIndex, cornerstoneViewportService);
viewportData,
viewportId,
imageIndex,
cornerstoneViewportService
);
} }
return null; return null;
}, [viewportData, viewportId, imageIndex, cornerstoneViewportService]); }, [viewportData, viewportId, imageIndex, cornerstoneViewportService]);
@ -178,10 +161,7 @@ function CustomizableViewportOverlay({
} }
const { lower, upper } = range; const { lower, upper } = range;
const { windowWidth, windowCenter } = utilities.windowLevel.toWindowLevel( const { windowWidth, windowCenter } = utilities.windowLevel.toWindowLevel(lower, upper);
lower,
upper
);
setVOI({ windowCenter, windowWidth }); setVOI({ windowCenter, windowWidth });
}; };
@ -204,9 +184,7 @@ function CustomizableViewportOverlay({
previousCamera.parallelScale !== camera.parallelScale || previousCamera.parallelScale !== camera.parallelScale ||
previousCamera.scale !== camera.scale previousCamera.scale !== camera.scale
) { ) {
const viewport = cornerstoneViewportService.getCornerstoneViewport( const viewport = cornerstoneViewportService.getCornerstoneViewport(viewportId);
viewportId
);
if (!viewport) { if (!viewport) {
return; return;
@ -225,8 +203,7 @@ function CustomizableViewportOverlay({
const { spacing } = imageData; const { spacing } = imageData;
// convert parallel scale to scale // convert parallel scale to scale
const scale = const scale = (element.clientHeight * spacing[0] * 0.5) / camera.parallelScale;
(element.clientHeight * spacing[0] * 0.5) / camera.parallelScale;
setScale(scale); setScale(scale);
} }
}; };
@ -342,9 +319,7 @@ function CustomizableViewportOverlay({
return ( return (
<> <>
{items.map((item, i) => ( {items.map((item, i) => (
<div key={`bottomLeftOverlayItem_${i}`}> <div key={`bottomLeftOverlayItem_${i}`}>{_renderOverlayItem(item)}</div>
{_renderOverlayItem(item)}
</div>
))} ))}
</> </>
); );
@ -355,9 +330,7 @@ function CustomizableViewportOverlay({
return ( return (
<> <>
{items.map((item, i) => ( {items.map((item, i) => (
<div key={`bottomRightOverlayItem_${i}`}> <div key={`bottomRightOverlayItem_${i}`}>{_renderOverlayItem(item)}</div>
{_renderOverlayItem(item)}
</div>
))} ))}
</> </>
); );
@ -387,12 +360,7 @@ function _getViewportInstance(viewportData, imageIndex) {
return imageId ? metaData.get('instance', imageId) || {} : {}; return imageId ? metaData.get('instance', imageId) || {} : {};
} }
function _getInstanceNumber( function _getInstanceNumber(viewportData, viewportId, imageIndex, cornerstoneViewportService) {
viewportData,
viewportId,
imageIndex,
cornerstoneViewportService
) {
let instanceNumber; let instanceNumber;
if (viewportData.viewportType === Enums.ViewportType.STACK) { if (viewportData.viewportType === Enums.ViewportType.STACK) {
@ -435,11 +403,7 @@ function _getInstanceNumberFromStack(viewportData, imageIndex) {
// Since volume viewports can be in any view direction, they can render // Since volume viewports can be in any view direction, they can render
// a reconstructed image which don't have imageIds; therefore, no instance and instanceNumber // a reconstructed image which don't have imageIds; therefore, no instance and instanceNumber
// Here we check if viewport is in the acquisition direction and if so, we get the instanceNumber // Here we check if viewport is in the acquisition direction and if so, we get the instanceNumber
function _getInstanceNumberFromVolume( function _getInstanceNumberFromVolume(viewportData, viewportId, cornerstoneViewportService) {
viewportData,
viewportId,
cornerstoneViewportService
) {
const volumes = viewportData.volumes; const volumes = viewportData.volumes;
// Todo: support fusion of acquisition plane which has instanceNumber // Todo: support fusion of acquisition plane which has instanceNumber
@ -450,9 +414,7 @@ function _getInstanceNumberFromVolume(
const volume = volumes[0]; const volume = volumes[0];
const { direction, imageIds } = volume; const { direction, imageIds } = volume;
const cornerstoneViewport = cornerstoneViewportService.getCornerstoneViewport( const cornerstoneViewport = cornerstoneViewportService.getCornerstoneViewport(viewportId);
viewportId
);
if (!cornerstoneViewport) { if (!cornerstoneViewport) {
return; return;
@ -475,8 +437,7 @@ function _getInstanceNumberFromVolume(
return {}; return {};
} }
const { instanceNumber } = const { instanceNumber } = metaData.get('generalImageModule', imageId) || {};
metaData.get('generalImageModule', imageId) || {};
return parseInt(instanceNumber); return parseInt(instanceNumber);
} }
} }

View File

@ -14,15 +14,10 @@ function CornerstoneImageScrollbar({
scrollbarHeight, scrollbarHeight,
servicesManager, servicesManager,
}) { }) {
const { const { cineService, cornerstoneViewportService } = (servicesManager as ServicesManger).services;
cineService,
cornerstoneViewportService,
} = (servicesManager as ServicesManger).services;
const onImageScrollbarChange = (imageIndex, viewportId) => { const onImageScrollbarChange = (imageIndex, viewportId) => {
const viewport = cornerstoneViewportService.getCornerstoneViewport( const viewport = cornerstoneViewportService.getCornerstoneViewport(viewportId);
viewportId
);
const { isCineEnabled } = cineService.getState(); const { isCineEnabled } = cineService.getState();
@ -43,9 +38,7 @@ function CornerstoneImageScrollbar({
return; return;
} }
const viewport = cornerstoneViewportService.getCornerstoneViewport( const viewport = cornerstoneViewportService.getCornerstoneViewport(viewportId);
viewportId
);
if (!viewport) { if (!viewport) {
return; return;
@ -90,16 +83,10 @@ function CornerstoneImageScrollbar({
}); });
}; };
element.addEventListener( element.addEventListener(Enums.Events.STACK_VIEWPORT_SCROLL, updateStackIndex);
Enums.Events.STACK_VIEWPORT_SCROLL,
updateStackIndex
);
return () => { return () => {
element.removeEventListener( element.removeEventListener(Enums.Events.STACK_VIEWPORT_SCROLL, updateStackIndex);
Enums.Events.STACK_VIEWPORT_SCROLL,
updateStackIndex
);
}; };
}, [viewportData, element]); }, [viewportData, element]);
@ -117,19 +104,14 @@ function CornerstoneImageScrollbar({
element.addEventListener(Enums.Events.VOLUME_NEW_IMAGE, updateVolumeIndex); element.addEventListener(Enums.Events.VOLUME_NEW_IMAGE, updateVolumeIndex);
return () => { return () => {
element.removeEventListener( element.removeEventListener(Enums.Events.VOLUME_NEW_IMAGE, updateVolumeIndex);
Enums.Events.VOLUME_NEW_IMAGE,
updateVolumeIndex
);
}; };
}, [viewportData, element]); }, [viewportData, element]);
return ( return (
<ImageScrollbar <ImageScrollbar
onChange={evt => onImageScrollbarChange(evt, viewportId)} onChange={evt => onImageScrollbarChange(evt, viewportId)}
max={ max={imageSliceData.numberOfSlices ? imageSliceData.numberOfSlices - 1 : 0}
imageSliceData.numberOfSlices ? imageSliceData.numberOfSlices - 1 : 0
}
height={scrollbarHeight} height={scrollbarHeight}
value={imageSliceData.imageIndex} value={imageSliceData.imageIndex}
/> />

View File

@ -33,26 +33,14 @@ function ViewportImageSliceLoadingIndicator({ viewportData, element }) {
}; };
useEffect(() => { useEffect(() => {
element.addEventListener( element.addEventListener(Enums.Events.STACK_VIEWPORT_SCROLL, setLoadingState);
Enums.Events.STACK_VIEWPORT_SCROLL,
setLoadingState
);
element.addEventListener(Enums.Events.IMAGE_LOAD_ERROR, setErrorState); element.addEventListener(Enums.Events.IMAGE_LOAD_ERROR, setErrorState);
element.addEventListener( element.addEventListener(Enums.Events.STACK_NEW_IMAGE, setFinishLoadingState);
Enums.Events.STACK_NEW_IMAGE,
setFinishLoadingState
);
return () => { return () => {
element.removeEventListener( element.removeEventListener(Enums.Events.STACK_VIEWPORT_SCROLL, setLoadingState);
Enums.Events.STACK_VIEWPORT_SCROLL,
setLoadingState
);
element.removeEventListener( element.removeEventListener(Enums.Events.STACK_NEW_IMAGE, setFinishLoadingState);
Enums.Events.STACK_NEW_IMAGE,
setFinishLoadingState
);
element.removeEventListener(Enums.Events.IMAGE_LOAD_ERROR, setErrorState); element.removeEventListener(Enums.Events.IMAGE_LOAD_ERROR, setErrorState);
}; };
@ -61,8 +49,8 @@ function ViewportImageSliceLoadingIndicator({ viewportData, element }) {
if (error) { if (error) {
return ( return (
<> <>
<div className="bg-black opacity-50 absolute h-full w-full top-0 left-0"> <div className="absolute top-0 left-0 h-full w-full bg-black opacity-50">
<div className="flex transparent items-center justify-center w-full h-full"> <div className="transparent flex h-full w-full items-center justify-center">
<p className="text-primary-light text-xl font-light"> <p className="text-primary-light text-xl font-light">
<h4>Error Loading Image</h4> <h4>Error Loading Image</h4>
<p>An error has occurred.</p> <p>An error has occurred.</p>
@ -78,8 +66,8 @@ function ViewportImageSliceLoadingIndicator({ viewportData, element }) {
return ( return (
// IMPORTANT: we need to use the pointer-events-none class to prevent the loading indicator from // IMPORTANT: we need to use the pointer-events-none class to prevent the loading indicator from
// interacting with the mouse, since scrolling should propagate to the viewport underneath // interacting with the mouse, since scrolling should propagate to the viewport underneath
<div className="pointer-events-none bg-black opacity-50 absolute h-full w-full top-0 left-0"> <div className="pointer-events-none absolute top-0 left-0 h-full w-full bg-black opacity-50">
<div className="flex transparent items-center justify-center w-full h-full"> <div className="transparent flex h-full w-full items-center justify-center">
<p className="text-primary-light text-xl font-light">Loading...</p> <p className="text-primary-light text-xl font-light">Loading...</p>
</div> </div>
</div> </div>

View File

@ -13,10 +13,7 @@ import { vec3 } from 'gl-matrix';
import './ViewportOrientationMarkers.css'; import './ViewportOrientationMarkers.css';
const { const { getOrientationStringLPS, invertOrientationStringLPS } = utilities.orientation;
getOrientationStringLPS,
invertOrientationStringLPS,
} = utilities.orientation;
function ViewportOrientationMarkers({ function ViewportOrientationMarkers({
element, element,
@ -33,9 +30,7 @@ function ViewportOrientationMarkers({
const { cornerstoneViewportService } = servicesManager.services; const { cornerstoneViewportService } = servicesManager.services;
useEffect(() => { useEffect(() => {
const cameraModifiedListener = ( const cameraModifiedListener = (evt: Types.EventTypes.CameraModifiedEvent) => {
evt: Types.EventTypes.CameraModifiedEvent
) => {
const { rotation, previousCamera, camera } = evt.detail; const { rotation, previousCamera, camera } = evt.detail;
if (rotation !== undefined) { if (rotation !== undefined) {
@ -57,16 +52,10 @@ function ViewportOrientationMarkers({
} }
}; };
element.addEventListener( element.addEventListener(Enums.Events.CAMERA_MODIFIED, cameraModifiedListener);
Enums.Events.CAMERA_MODIFIED,
cameraModifiedListener
);
return () => { return () => {
element.removeEventListener( element.removeEventListener(Enums.Events.CAMERA_MODIFIED, cameraModifiedListener);
Enums.Events.CAMERA_MODIFIED,
cameraModifiedListener
);
}; };
}, []); }, []);
@ -85,8 +74,7 @@ function ViewportOrientationMarkers({
return false; return false;
} }
({ rowCosines, columnCosines } = ({ rowCosines, columnCosines } = metaData.get('imagePlaneModule', imageId) || {});
metaData.get('imagePlaneModule', imageId) || {});
} else { } else {
if (!element || !getEnabledElement(element)) { if (!element || !getEnabledElement(element)) {
return ''; return '';
@ -124,9 +112,7 @@ function ViewportOrientationMarkers({
// Todo: probably this can be done in a better way in which we identify bright // Todo: probably this can be done in a better way in which we identify bright
// background // background
const isLight = backgroundColor const isLight = backgroundColor ? csUtils.isEqual(backgroundColor, [1, 1, 1]) : false;
? csUtils.isEqual(backgroundColor, [1, 1, 1])
: false;
return orientationMarkers.map((m, index) => ( return orientationMarkers.map((m, index) => (
<div <div
@ -172,13 +158,7 @@ ViewportOrientationMarkers.defaultProps = {
* @param {*} rotation in degrees * @param {*} rotation in degrees
* @returns * @returns
*/ */
function _getOrientationMarkers( function _getOrientationMarkers(rowCosines, columnCosines, rotation, flipVertical, flipHorizontal) {
rowCosines,
columnCosines,
rotation,
flipVertical,
flipHorizontal
) {
const rowString = getOrientationStringLPS(rowCosines); const rowString = getOrientationStringLPS(rowCosines);
const columnString = getOrientationStringLPS(columnCosines); const columnString = getOrientationStringLPS(columnCosines);
const oppositeRowString = invertOrientationStringLPS(rowString); const oppositeRowString = invertOrientationStringLPS(rowString);

View File

@ -14,10 +14,7 @@ function CornerstoneViewportOverlay({
viewportId, viewportId,
servicesManager, servicesManager,
}) { }) {
const { const { cornerstoneViewportService, toolbarService } = servicesManager.services;
cornerstoneViewportService,
toolbarService,
} = servicesManager.services;
const [voi, setVOI] = useState({ windowCenter: null, windowWidth: null }); const [voi, setVOI] = useState({ windowCenter: null, windowWidth: null });
const [scale, setScale] = useState(1); const [scale, setScale] = useState(1);
const [activeTools, setActiveTools] = useState([]); const [activeTools, setActiveTools] = useState([]);
@ -60,10 +57,7 @@ function CornerstoneViewportOverlay({
} }
const { lower, upper } = range; const { lower, upper } = range;
const { windowWidth, windowCenter } = utilities.windowLevel.toWindowLevel( const { windowWidth, windowCenter } = utilities.windowLevel.toWindowLevel(lower, upper);
lower,
upper
);
setVOI({ windowCenter, windowWidth }); setVOI({ windowCenter, windowWidth });
}; };
@ -86,9 +80,7 @@ function CornerstoneViewportOverlay({
previousCamera.parallelScale !== camera.parallelScale || previousCamera.parallelScale !== camera.parallelScale ||
previousCamera.scale !== camera.scale previousCamera.scale !== camera.scale
) { ) {
const viewport = cornerstoneViewportService.getCornerstoneViewport( const viewport = cornerstoneViewportService.getCornerstoneViewport(viewportId);
viewportId
);
if (!viewport) { if (!viewport) {
return; return;
@ -107,8 +99,7 @@ function CornerstoneViewportOverlay({
const { spacing } = imageData; const { spacing } = imageData;
// convert parallel scale to scale // convert parallel scale to scale
const scale = const scale = (element.clientHeight * spacing[0] * 0.5) / camera.parallelScale;
(element.clientHeight * spacing[0] * 0.5) / camera.parallelScale;
setScale(scale); setScale(scale);
} }
}; };
@ -199,9 +190,7 @@ function CornerstoneViewportOverlay({
// Todo: probably this can be done in a better way in which we identify bright // Todo: probably this can be done in a better way in which we identify bright
// background // background
const isLight = backgroundColor const isLight = backgroundColor ? utilities.isEqual(backgroundColor, [1, 1, 1]) : false;
? utilities.isEqual(backgroundColor, [1, 1, 1])
: false;
return ( return (
<ViewportOverlay <ViewportOverlay
@ -251,9 +240,7 @@ function _getInstanceNumberFromVolume(
const volume = volumes[0]; const volume = volumes[0];
const { direction, imageIds } = volume; const { direction, imageIds } = volume;
const cornerstoneViewport = cornerstoneViewportService.getCornerstoneViewport( const cornerstoneViewport = cornerstoneViewportService.getCornerstoneViewport(viewportId);
viewportId
);
if (!cornerstoneViewport) { if (!cornerstoneViewport) {
return; return;
@ -276,8 +263,7 @@ function _getInstanceNumberFromVolume(
return {}; return {};
} }
const { instanceNumber } = const { instanceNumber } = metaData.get('generalImageModule', imageId) || {};
metaData.get('generalImageModule', imageId) || {};
return parseInt(instanceNumber); return parseInt(instanceNumber);
} }
} }

View File

@ -79,18 +79,12 @@ export function formatPN(name) {
*/ */
export function getCompression(imageId) { export function getCompression(imageId) {
const generalImageModule = metaData.get('generalImageModule', imageId) || {}; const generalImageModule = metaData.get('generalImageModule', imageId) || {};
const { const { lossyImageCompression, lossyImageCompressionRatio, lossyImageCompressionMethod } =
lossyImageCompression, generalImageModule;
lossyImageCompressionRatio,
lossyImageCompressionMethod,
} = generalImageModule;
if (lossyImageCompression === '01' && lossyImageCompressionRatio !== '') { if (lossyImageCompression === '01' && lossyImageCompressionRatio !== '') {
const compressionMethod = lossyImageCompressionMethod || 'Lossy: '; const compressionMethod = lossyImageCompressionMethod || 'Lossy: ';
const compressionRatio = formatNumberPrecision( const compressionRatio = formatNumberPrecision(lossyImageCompressionRatio, 2);
lossyImageCompressionRatio,
2
);
return compressionMethod + compressionRatio + ' : 1'; return compressionMethod + compressionRatio + ' : 1';
} }

View File

@ -57,9 +57,7 @@ function commandsModule({
// filter by allowed selected tools from config property (if there is any) // filter by allowed selected tools from config property (if there is any)
const isToolAllowed = const isToolAllowed =
!optionsToUse.allowedSelectedTools || !optionsToUse.allowedSelectedTools ||
optionsToUse.allowedSelectedTools.includes( optionsToUse.allowedSelectedTools.includes(firstAnnotationSelected?.metadata?.toolName);
firstAnnotationSelected?.metadata?.toolName
);
if (isToolAllowed) { if (isToolAllowed) {
optionsToUse.nearbyToolData = firstAnnotationSelected; optionsToUse.nearbyToolData = firstAnnotationSelected;
} else { } else {
@ -89,10 +87,7 @@ function commandsModule({
}, },
getNearbyToolData({ nearbyToolData, element, canvasCoordinates }) { getNearbyToolData({ nearbyToolData, element, canvasCoordinates }) {
return ( return nearbyToolData ?? cstUtils.getAnnotationNearPoint(element, canvasCoordinates);
nearbyToolData ??
cstUtils.getAnnotationNearPoint(element, canvasCoordinates)
);
}, },
getNearbyAnnotation({ element, canvasCoordinates }) { getNearbyAnnotation({ element, canvasCoordinates }) {
const nearbyToolData = actions.getNearbyToolData({ const nearbyToolData = actions.getNearbyToolData({
@ -109,18 +104,14 @@ function commandsModule({
} }
const { renderingEngineId, viewportId } = enabledElement; const { renderingEngineId, viewportId } = enabledElement;
const toolGroup = ToolGroupManager.getToolGroupForViewport( const toolGroup = ToolGroupManager.getToolGroupForViewport(viewportId, renderingEngineId);
viewportId,
renderingEngineId
);
const toolInstance = toolGroup.getToolInstance(toolName); const toolInstance = toolGroup.getToolInstance(toolName);
return toolInstance?.constructor?.isAnnotation ?? true; return toolInstance?.constructor?.isAnnotation ?? true;
}; };
return nearbyToolData?.metadata?.toolName && return nearbyToolData?.metadata?.toolName && isAnnotation(nearbyToolData.metadata.toolName)
isAnnotation(nearbyToolData.metadata.toolName)
? nearbyToolData ? nearbyToolData
: null; : null;
}, },
@ -153,11 +144,7 @@ function commandsModule({
label, label,
}); });
measurementService.update( measurementService.update(updatedMeasurement.uid, updatedMeasurement, true);
updatedMeasurement.uid,
updatedMeasurement,
true
);
}, },
false false
); );
@ -220,20 +207,14 @@ function commandsModule({
} }
} }
} }
measurementService.update( measurementService.update(updatedMeasurement.uid, updatedMeasurement, true);
updatedMeasurement.uid,
updatedMeasurement,
true
);
}, },
// Retrieve value commands // Retrieve value commands
getActiveViewportEnabledElement: _getActiveViewportEnabledElement, getActiveViewportEnabledElement: _getActiveViewportEnabledElement,
setViewportActive: ({ viewportId }) => { setViewportActive: ({ viewportId }) => {
const viewportInfo = cornerstoneViewportService.getViewportInfo( const viewportInfo = cornerstoneViewportService.getViewportInfo(viewportId);
viewportId
);
if (!viewportInfo) { if (!viewportInfo) {
console.warn('No viewport found for viewportId:', viewportId); console.warn('No viewport found for viewportId:', viewportId);
return; return;
@ -249,9 +230,7 @@ function commandsModule({
const { isCineEnabled } = cineService.getState(); const { isCineEnabled } = cineService.getState();
cineService.setIsCineEnabled(!isCineEnabled); cineService.setIsCineEnabled(!isCineEnabled);
toolbarService.setButton('Cine', { props: { isActive: !isCineEnabled } }); toolbarService.setButton('Cine', { props: { isActive: !isCineEnabled } });
viewports.forEach((_, index) => viewports.forEach((_, index) => cineService.setCine({ id: index, isPlaying: false }));
cineService.setCine({ id: index, isPlaying: false })
);
}, },
setWindowLevel({ window, level, toolGroupId }) { setWindowLevel({ window, level, toolGroupId }) {
// convert to numbers // convert to numbers
@ -259,9 +238,7 @@ function commandsModule({
const windowCenterNum = Number(level); const windowCenterNum = Number(level);
const { viewportId } = _getActiveViewportEnabledElement(); const { viewportId } = _getActiveViewportEnabledElement();
const viewportToolGroupId = toolGroupService.getToolGroupForViewport( const viewportToolGroupId = toolGroupService.getToolGroupForViewport(viewportId);
viewportId
);
if (toolGroupId && toolGroupId !== viewportToolGroupId) { if (toolGroupId && toolGroupId !== viewportToolGroupId) {
return; return;
@ -271,10 +248,7 @@ function commandsModule({
const renderingEngine = cornerstoneViewportService.getRenderingEngine(); const renderingEngine = cornerstoneViewportService.getRenderingEngine();
const viewport = renderingEngine.getViewport(viewportId); const viewport = renderingEngine.getViewport(viewportId);
const { lower, upper } = csUtils.windowLevel.toLowHighRange( const { lower, upper } = csUtils.windowLevel.toLowHighRange(windowWidthNum, windowCenterNum);
windowWidthNum,
windowCenterNum
);
viewport.setProperties({ viewport.setProperties({
voiRange: { voiRange: {
@ -323,11 +297,9 @@ function commandsModule({
return; return;
} }
const filteredViewports = Array.from(viewports.values()).filter( const filteredViewports = Array.from(viewports.values()).filter(viewport => {
viewport => {
return toolGroupViewportIds.includes(viewport.viewportId); return toolGroupViewportIds.includes(viewport.viewportId);
} });
);
if (!filteredViewports.length) { if (!filteredViewports.length) {
return; return;
@ -367,9 +339,7 @@ function commandsModule({
showDownloadViewportModal: () => { showDownloadViewportModal: () => {
const { activeViewportId } = viewportGridService.getState(); const { activeViewportId } = viewportGridService.getState();
if ( if (!cornerstoneViewportService.getCornerstoneViewport(activeViewportId)) {
!cornerstoneViewportService.getCornerstoneViewport(activeViewportId)
) {
// Cannot download a non-cornerstone viewport (image). // Cannot download a non-cornerstone viewport (image).
uiNotificationService.show({ uiNotificationService.show({
title: 'Download Image', title: 'Download Image',
@ -508,9 +478,7 @@ function commandsModule({
} }
viewport = enabledElement.viewport; viewport = enabledElement.viewport;
} else { } else {
viewport = cornerstoneViewportService.getCornerstoneViewport( viewport = cornerstoneViewportService.getCornerstoneViewport(gridViewport.id);
gridViewport.id
);
} }
// Get number of slices // Get number of slices
@ -520,14 +488,12 @@ function commandsModule({
if (viewport instanceof StackViewport) { if (viewport instanceof StackViewport) {
numberOfSlices = viewport.getImageIds().length; numberOfSlices = viewport.getImageIds().length;
} else if (viewport instanceof VolumeViewport) { } else if (viewport instanceof VolumeViewport) {
numberOfSlices = csUtils.getImageSliceDataForVolumeViewport(viewport) numberOfSlices = csUtils.getImageSliceDataForVolumeViewport(viewport).numberOfSlices;
.numberOfSlices;
} else { } else {
throw new Error('Unsupported viewport type'); throw new Error('Unsupported viewport type');
} }
const jumpIndex = const jumpIndex = imageIndex < 0 ? numberOfSlices + imageIndex : imageIndex;
imageIndex < 0 ? numberOfSlices + imageIndex : imageIndex;
if (jumpIndex >= numberOfSlices || jumpIndex < 0) { if (jumpIndex >= numberOfSlices || jumpIndex < 0) {
throw new Error(`Can't jump to ${imageIndex}`); throw new Error(`Can't jump to ${imageIndex}`);
} }
@ -548,15 +514,8 @@ function commandsModule({
cstUtils.scroll(viewport, options); cstUtils.scroll(viewport, options);
}, },
setViewportColormap: ({ setViewportColormap: ({ viewportId, displaySetInstanceUID, colormap, immediate = false }) => {
viewportId, const viewport = cornerstoneViewportService.getCornerstoneViewport(viewportId);
displaySetInstanceUID,
colormap,
immediate = false,
}) => {
const viewport = cornerstoneViewportService.getCornerstoneViewport(
viewportId
);
const actorEntries = viewport.getActors(); const actorEntries = viewport.getActors();
@ -578,9 +537,7 @@ function commandsModule({
const currentIndex = viewportIds.indexOf(activeViewportId); const currentIndex = viewportIds.indexOf(activeViewportId);
const nextViewportIndex = const nextViewportIndex =
(currentIndex + direction + viewportIds.length) % viewportIds.length; (currentIndex + direction + viewportIds.length) % viewportIds.length;
viewportGridService.setActiveViewportId( viewportGridService.setActiveViewportId(viewportIds[nextViewportIndex] as string);
viewportIds[nextViewportIndex] as string
);
}, },
toggleStackImageSync: ({ toggledState }) => { toggleStackImageSync: ({ toggledState }) => {
@ -592,9 +549,7 @@ function commandsModule({
}, },
toggleReferenceLines: ({ toggledState }) => { toggleReferenceLines: ({ toggledState }) => {
const { activeViewportId } = viewportGridService.getState(); const { activeViewportId } = viewportGridService.getState();
const viewportInfo = cornerstoneViewportService.getViewportInfo( const viewportInfo = cornerstoneViewportService.getViewportInfo(activeViewportId);
activeViewportId
);
const viewportId = viewportInfo.getViewportId(); const viewportId = viewportInfo.getViewportId();
const toolGroup = toolGroupService.getToolGroupForViewport(viewportId); const toolGroup = toolGroupService.getToolGroupForViewport(viewportId);

View File

@ -46,17 +46,11 @@ function WrappedCinePlayer({ enabledVPElement, viewportId, servicesManager }) {
}; };
useEffect(() => { useEffect(() => {
eventTarget.addEventListener( eventTarget.addEventListener(Enums.Events.STACK_VIEWPORT_NEW_STACK, cineHandler);
Enums.Events.STACK_VIEWPORT_NEW_STACK,
cineHandler
);
return () => { return () => {
cineService.setCine({ id: viewportId, isPlaying: false }); cineService.setCine({ id: viewportId, isPlaying: false });
eventTarget.removeEventListener( eventTarget.removeEventListener(Enums.Events.STACK_VIEWPORT_NEW_STACK, cineHandler);
Enums.Events.STACK_VIEWPORT_NEW_STACK,
cineHandler
);
}; };
}, [enabledVPElement]); }, [enabledVPElement]);
@ -80,7 +74,7 @@ function WrappedCinePlayer({ enabledVPElement, viewportId, servicesManager }) {
return ( return (
isCineEnabled && ( isCineEnabled && (
<CinePlayerComponent <CinePlayerComponent
className="absolute left-1/2 -translate-x-1/2 bottom-3" className="absolute left-1/2 bottom-3 -translate-x-1/2"
isPlaying={isPlaying} isPlaying={isPlaying}
onClose={handleCineClose} onClose={handleCineClose}
onPlayPauseChange={isPlaying => onPlayPauseChange={isPlaying =>

View File

@ -1,6 +1,23 @@
.dicom-upload-drop-area-border-dash { .dicom-upload-drop-area-border-dash {
background-image: repeating-linear-gradient(to right, #7BB2CE 0%, #7BB2CE 50%, transparent 50%, transparent 100%), repeating-linear-gradient(to right, #7BB2CE 0%, #7BB2CE 50%, transparent 50%, transparent 100%), repeating-linear-gradient(to bottom, #7BB2CE 0%, #7BB2CE 50%, transparent 50%, transparent 100%), repeating-linear-gradient(to bottom, #7BB2CE 0%, #7BB2CE 50%, transparent 50%, transparent 100%); background-image: repeating-linear-gradient(
background-position: left top, left bottom, left top, right top; to right,
#7bb2ce 0%,
#7bb2ce 50%,
transparent 50%,
transparent 100%
),
repeating-linear-gradient(to right, #7bb2ce 0%, #7bb2ce 50%, transparent 50%, transparent 100%),
repeating-linear-gradient(to bottom, #7bb2ce 0%, #7bb2ce 50%, transparent 50%, transparent 100%),
repeating-linear-gradient(to bottom, #7bb2ce 0%, #7bb2ce 50%, transparent 50%, transparent 100%);
background-position:
left top,
left bottom,
left top,
right top;
background-repeat: repeat-x, repeat-x, repeat-y, repeat-y; background-repeat: repeat-x, repeat-x, repeat-y, repeat-y;
background-size: 20px 3px, 20px 3px, 3px 20px, 3px 20px; background-size:
20px 3px,
20px 3px,
3px 20px,
3px 20px;
} }

View File

@ -14,19 +14,13 @@ type DicomUploadProps = {
onStarted: () => void; onStarted: () => void;
}; };
function DicomUpload({ function DicomUpload({ dataSource, onComplete, onStarted }: DicomUploadProps): ReactElement {
dataSource,
onComplete,
onStarted,
}: DicomUploadProps): ReactElement {
const baseClassNames = 'min-h-[480px] flex flex-col bg-black select-none'; const baseClassNames = 'min-h-[480px] flex flex-col bg-black select-none';
const [dicomFileUploaderArr, setDicomFileUploaderArr] = useState([]); const [dicomFileUploaderArr, setDicomFileUploaderArr] = useState([]);
const onDrop = useCallback(async acceptedFiles => { const onDrop = useCallback(async acceptedFiles => {
onStarted(); onStarted();
setDicomFileUploaderArr( setDicomFileUploaderArr(acceptedFiles.map(file => new DicomFileUploader(file, dataSource)));
acceptedFiles.map(file => new DicomFileUploader(file, dataSource))
);
}, []); }, []);
const getDropZoneComponent = (): ReactElement => { const getDropZoneComponent = (): ReactElement => {
@ -40,20 +34,29 @@ function DicomUpload({
{({ getRootProps }) => ( {({ getRootProps }) => (
<div <div
{...getRootProps()} {...getRootProps()}
className="m-5 dicom-upload-drop-area-border-dash flex flex-col items-center justify-center h-full" className="dicom-upload-drop-area-border-dash m-5 flex h-full flex-col items-center justify-center"
> >
<div className="flex gap-3"> <div className="flex gap-3">
<Dropzone onDrop={onDrop} noDrag> <Dropzone
onDrop={onDrop}
noDrag
>
{({ getRootProps, getInputProps }) => ( {({ getRootProps, getInputProps }) => (
<div {...getRootProps()}> <div {...getRootProps()}>
<Button disabled={false} onClick={() => {}}> <Button
disabled={false}
onClick={() => {}}
>
{'Add files'} {'Add files'}
<input {...getInputProps()} /> <input {...getInputProps()} />
</Button> </Button>
</div> </div>
)} )}
</Dropzone> </Dropzone>
<Dropzone onDrop={onDrop} noDrag> <Dropzone
onDrop={onDrop}
noDrag
>
{({ getRootProps, getInputProps }) => ( {({ getRootProps, getInputProps }) => (
<div {...getRootProps()}> <div {...getRootProps()}>
<Button <Button
@ -73,9 +76,7 @@ function DicomUpload({
</Dropzone> </Dropzone>
</div> </div>
<div className="pt-5">or drag images or folders here</div> <div className="pt-5">or drag images or folders here</div>
<div className="pt-3 text-aqua-pale text-lg"> <div className="text-aqua-pale pt-3 text-lg">(DICOM files supported)</div>
(DICOM files supported)
</div>
</div> </div>
)} )}
</Dropzone> </Dropzone>
@ -92,9 +93,7 @@ function DicomUpload({
/> />
</div> </div>
) : ( ) : (
<div className={classNames('h-[480px]', baseClassNames)}> <div className={classNames('h-[480px]', baseClassNames)}>{getDropZoneComponent()}</div>
{getDropZoneComponent()}
</div>
)} )}
</> </>
); );

View File

@ -1,10 +1,4 @@
import React, { import React, { useCallback, useEffect, useRef, useState, ReactElement } from 'react';
useCallback,
useEffect,
useRef,
useState,
ReactElement,
} from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { Button, Icon, ProgressLoadingBar } from '@ohif/ui'; import { Button, Icon, ProgressLoadingBar } from '@ohif/ui';
import DicomFileUploader, { import DicomFileUploader, {
@ -39,18 +33,14 @@ const BASE_INTERVAL_TIME = 15000;
// calculate the upload rate. // calculate the upload rate.
const UPLOAD_RATE_THRESHOLD = 75; const UPLOAD_RATE_THRESHOLD = 75;
const NO_WRAP_ELLIPSIS_CLASS_NAMES = const NO_WRAP_ELLIPSIS_CLASS_NAMES = 'text-ellipsis whitespace-nowrap overflow-hidden';
'text-ellipsis whitespace-nowrap overflow-hidden';
function DicomUploadProgress({ function DicomUploadProgress({
dicomFileUploaderArr, dicomFileUploaderArr,
onComplete, onComplete,
}: DicomUploadProgressProps): ReactElement { }: DicomUploadProgressProps): ReactElement {
const [totalUploadSize] = useState( const [totalUploadSize] = useState(
dicomFileUploaderArr.reduce( dicomFileUploaderArr.reduce((acc, fileUploader) => acc + fileUploader.getFileSize(), 0)
(acc, fileUploader) => acc + fileUploader.getFileSize(),
0
)
); );
const currentUploadSizeRef = useRef<number>(0); const currentUploadSizeRef = useRef<number>(0);
@ -83,15 +73,13 @@ function DicomUploadProgress({
let intervalStartTime = Date.now(); let intervalStartTime = Date.now();
const setUploadRateRef = () => { const setUploadRateRef = () => {
const uploadSizeFromStartOfInterval = const uploadSizeFromStartOfInterval = currentUploadSizeRef.current - intervalStartUploadSize;
currentUploadSizeRef.current - intervalStartUploadSize;
const now = Date.now(); const now = Date.now();
const timeSinceStartOfInterval = now - intervalStartTime; const timeSinceStartOfInterval = now - intervalStartTime;
// Calculate and set the upload rate (ref) // Calculate and set the upload rate (ref)
uploadRateRef.current = uploadRateRef.current = uploadSizeFromStartOfInterval / timeSinceStartOfInterval;
uploadSizeFromStartOfInterval / timeSinceStartOfInterval;
// Reset the interval starting values. // Reset the interval starting values.
intervalStartUploadSize = currentUploadSizeRef.current; intervalStartUploadSize = currentUploadSizeRef.current;
@ -134,28 +122,19 @@ function DicomUploadProgress({
const updateProgress = (percentComplete: number) => { const updateProgress = (percentComplete: number) => {
const previousFileUploadSize = currentFileUploadSize; const previousFileUploadSize = currentFileUploadSize;
currentFileUploadSize = Math.round( currentFileUploadSize = Math.round((percentComplete / 100) * fileUploader.getFileSize());
(percentComplete / 100) * fileUploader.getFileSize()
);
currentUploadSizeRef.current = Math.min( currentUploadSizeRef.current = Math.min(
totalUploadSize, totalUploadSize,
currentUploadSizeRef.current - currentUploadSizeRef.current - previousFileUploadSize + currentFileUploadSize
previousFileUploadSize +
currentFileUploadSize
); );
setPercentComplete( setPercentComplete((currentUploadSizeRef.current / totalUploadSize) * 100);
(currentUploadSizeRef.current / totalUploadSize) * 100
);
if (uploadRateRef.current !== 0) { if (uploadRateRef.current !== 0) {
const uploadSizeRemaining = const uploadSizeRemaining = totalUploadSize - currentUploadSizeRef.current;
totalUploadSize - currentUploadSizeRef.current;
const timeRemaining = Math.round( const timeRemaining = Math.round(uploadSizeRemaining / uploadRateRef.current);
uploadSizeRemaining / uploadRateRef.current
);
if (currentTimeRemaining === null) { if (currentTimeRemaining === null) {
currentTimeRemaining = timeRemaining; currentTimeRemaining = timeRemaining;
@ -168,9 +147,7 @@ function DicomUploadProgress({
// due to rounding, inaccuracies in the estimate and slight variations // due to rounding, inaccuracies in the estimate and slight variations
// in upload rates over time. // in upload rates over time.
if (timeRemaining < ONE_MINUTE) { if (timeRemaining < ONE_MINUTE) {
const currentSecondsRemaining = Math.ceil( const currentSecondsRemaining = Math.ceil(currentTimeRemaining / ONE_SECOND);
currentTimeRemaining / ONE_SECOND
);
const secondsRemaining = Math.ceil(timeRemaining / ONE_SECOND); const secondsRemaining = Math.ceil(timeRemaining / ONE_SECOND);
const delta = secondsRemaining - currentSecondsRemaining; const delta = secondsRemaining - currentSecondsRemaining;
if (delta < 0 || delta > 2) { if (delta < 0 || delta > 2) {
@ -181,9 +158,7 @@ function DicomUploadProgress({
} }
if (timeRemaining < ONE_HOUR) { if (timeRemaining < ONE_HOUR) {
const currentMinutesRemaining = Math.ceil( const currentMinutesRemaining = Math.ceil(currentTimeRemaining / ONE_MINUTE);
currentTimeRemaining / ONE_MINUTE
);
const minutesRemaining = Math.ceil(timeRemaining / ONE_MINUTE); const minutesRemaining = Math.ceil(timeRemaining / ONE_MINUTE);
const delta = minutesRemaining - currentMinutesRemaining; const delta = minutesRemaining - currentMinutesRemaining;
if (delta < 0 || delta > 2) { if (delta < 0 || delta > 2) {
@ -199,9 +174,7 @@ function DicomUploadProgress({
} }
}; };
const progressCallback = ( const progressCallback = (progressEvent: DicomFileUploaderProgressEvent) => {
progressEvent: DicomFileUploaderProgressEvent
) => {
updateProgress(progressEvent.percentComplete); updateProgress(progressEvent.percentComplete);
}; };
@ -249,16 +222,12 @@ function DicomUploadProgress({
if (timeRemaining < ONE_MINUTE) { if (timeRemaining < ONE_MINUTE) {
const secondsRemaining = Math.ceil(timeRemaining / ONE_SECOND); const secondsRemaining = Math.ceil(timeRemaining / ONE_SECOND);
return `${secondsRemaining} ${ return `${secondsRemaining} ${secondsRemaining === 1 ? 'second' : 'seconds'}`;
secondsRemaining === 1 ? 'second' : 'seconds'
}`;
} }
if (timeRemaining < ONE_HOUR) { if (timeRemaining < ONE_HOUR) {
const minutesRemaining = Math.ceil(timeRemaining / ONE_MINUTE); const minutesRemaining = Math.ceil(timeRemaining / ONE_MINUTE);
return `${minutesRemaining} ${ return `${minutesRemaining} ${minutesRemaining === 1 ? 'minute' : 'minutes'}`;
minutesRemaining === 1 ? 'minute' : 'minutes'
}`;
} }
const hoursRemaining = Math.ceil(timeRemaining / ONE_HOUR); const hoursRemaining = Math.ceil(timeRemaining / ONE_HOUR);
@ -278,9 +247,7 @@ function DicomUploadProgress({
const showInfiniteProgressBar = useCallback((): boolean => { const showInfiniteProgressBar = useCallback((): boolean => {
return ( return (
getPercentCompleteRounded() < 1 && getPercentCompleteRounded() < 1 &&
(progressBarContainerRef?.current?.offsetWidth ?? 0) * (progressBarContainerRef?.current?.offsetWidth ?? 0) * (percentComplete / 100) < 1
(percentComplete / 100) <
1
); );
}, [getPercentCompleteRounded, percentComplete]); }, [getPercentCompleteRounded, percentComplete]);
@ -300,15 +267,17 @@ function DicomUploadProgress({
const getNumCompletedAndTimeRemainingComponent = (): ReactElement => { const getNumCompletedAndTimeRemainingComponent = (): ReactElement => {
return ( return (
<div className="text-lg px-1 pb-4 h-14 flex bg-primary-dark items-center"> <div className="bg-primary-dark flex h-14 items-center px-1 pb-4 text-lg">
{numFilesCompleted === dicomFileUploaderArr.length ? ( {numFilesCompleted === dicomFileUploaderArr.length ? (
<> <>
<span className={NO_WRAP_ELLIPSIS_CLASS_NAMES}>{`${ <span className={NO_WRAP_ELLIPSIS_CLASS_NAMES}>{`${dicomFileUploaderArr.length} ${
dicomFileUploaderArr.length
} ${
dicomFileUploaderArr.length > 1 ? 'files' : 'file' dicomFileUploaderArr.length > 1 ? 'files' : 'file'
} completed.`}</span> } completed.`}</span>
<Button disabled={false} className="ml-auto" onClick={onComplete}> <Button
disabled={false}
className="ml-auto"
onClick={onComplete}
>
{'Close'} {'Close'}
</Button> </Button>
</> </>
@ -320,18 +289,14 @@ function DicomUploadProgress({
> >
{`${numFilesCompleted} of ${dicomFileUploaderArr.length}`}&nbsp; {`${numFilesCompleted} of ${dicomFileUploaderArr.length}`}&nbsp;
</span> </span>
<span className={NO_WRAP_ELLIPSIS_CLASS_NAMES}>{' files completed.'}&nbsp;</span>
<span className={NO_WRAP_ELLIPSIS_CLASS_NAMES}> <span className={NO_WRAP_ELLIPSIS_CLASS_NAMES}>
{' files completed.'}&nbsp; {timeRemaining ? `Less than ${getFormattedTimeRemaining()} remaining. ` : ''}
</span>
<span className={NO_WRAP_ELLIPSIS_CLASS_NAMES}>
{timeRemaining
? `Less than ${getFormattedTimeRemaining()} remaining. `
: ''}
</span> </span>
<span <span
className={classNames( className={classNames(
NO_WRAP_ELLIPSIS_CLASS_NAMES, NO_WRAP_ELLIPSIS_CLASS_NAMES,
'cursor-pointer text-primary-active hover:text-primary-light active:text-aqua-pale ml-auto' 'text-primary-active hover:text-primary-light active:text-aqua-pale ml-auto cursor-pointer'
)} )}
onClick={cancelAllUploads} onClick={cancelAllUploads}
> >
@ -345,14 +310,13 @@ function DicomUploadProgress({
const getShowFailedOnlyIconComponent = (): ReactElement => { const getShowFailedOnlyIconComponent = (): ReactElement => {
return ( return (
<div className="ml-auto flex justify-center w-6"> <div className="ml-auto flex w-6 justify-center">
{numFails > 0 && ( {numFails > 0 && (
<div <div onClick={() => setShowFailedOnly(currentShowFailedOnly => !currentShowFailedOnly)}>
onClick={() => <Icon
setShowFailedOnly(currentShowFailedOnly => !currentShowFailedOnly) className="cursor-pointer"
} name="icon-status-alert"
> ></Icon>
<Icon className="cursor-pointer" name="icon-status-alert"></Icon>
</div> </div>
)} )}
</div> </div>
@ -361,31 +325,28 @@ function DicomUploadProgress({
const getPercentCompleteComponent = (): ReactElement => { const getPercentCompleteComponent = (): ReactElement => {
return ( return (
<div className="overflow-y-scroll ohif-scrollbar px-2 border-b border-secondary-light"> <div className="ohif-scrollbar border-secondary-light overflow-y-scroll border-b px-2">
<div className="flex w-full p-2.5 items-center min-h-14"> <div className="min-h-14 flex w-full items-center p-2.5">
{numFilesCompleted === dicomFileUploaderArr.length ? ( {numFilesCompleted === dicomFileUploaderArr.length ? (
<> <>
<div className="text-xl text-primary-light"> <div className="text-primary-light text-xl">
{numFails > 0 {numFails > 0
? `Completed with ${numFails} ${ ? `Completed with ${numFails} ${numFails > 1 ? 'errors' : 'error'}!`
numFails > 1 ? 'errors' : 'error'
}!`
: 'Completed!'} : 'Completed!'}
</div> </div>
{getShowFailedOnlyIconComponent()} {getShowFailedOnlyIconComponent()}
</> </>
) : ( ) : (
<> <>
<div ref={progressBarContainerRef} className="flex-grow"> <div
ref={progressBarContainerRef}
className="flex-grow"
>
<ProgressLoadingBar <ProgressLoadingBar
progress={ progress={showInfiniteProgressBar() ? undefined : Math.min(100, percentComplete)}
showInfiniteProgressBar()
? undefined
: Math.min(100, percentComplete)
}
></ProgressLoadingBar> ></ProgressLoadingBar>
</div> </div>
<div className="w-24 ml-1 flex items-center"> <div className="ml-1 flex w-24 items-center">
<div className="w-10 text-right">{`${getPercentCompleteRounded()}%`}</div> <div className="w-10 text-right">{`${getPercentCompleteRounded()}%`}</div>
{getShowFailedOnlyIconComponent()} {getShowFailedOnlyIconComponent()}
</div> </div>
@ -397,16 +358,15 @@ function DicomUploadProgress({
}; };
return ( return (
<div className="flex flex-col grow"> <div className="flex grow flex-col">
{getNumCompletedAndTimeRemainingComponent()} {getNumCompletedAndTimeRemainingComponent()}
<div className="flex flex-col bg-black text-lg overflow-hidden grow"> <div className="flex grow flex-col overflow-hidden bg-black text-lg">
{getPercentCompleteComponent()} {getPercentCompleteComponent()}
<div className="overflow-y-scroll ohif-scrollbar px-2 grow h-1"> <div className="ohif-scrollbar h-1 grow overflow-y-scroll px-2">
{dicomFileUploaderArr {dicomFileUploaderArr
.filter( .filter(
dicomFileUploader => dicomFileUploader =>
!showFailedOnly || !showFailedOnly || dicomFileUploader.getStatus() === UploadStatus.Failed
dicomFileUploader.getStatus() === UploadStatus.Failed
) )
.map(dicomFileUploader => ( .map(dicomFileUploader => (
<DicomUploadProgressItem <DicomUploadProgressItem
@ -421,9 +381,7 @@ function DicomUploadProgress({
} }
DicomUploadProgress.propTypes = { DicomUploadProgress.propTypes = {
dicomFileUploaderArr: PropTypes.arrayOf( dicomFileUploaderArr: PropTypes.arrayOf(PropTypes.instanceOf(DicomFileUploader)).isRequired,
PropTypes.instanceOf(DicomFileUploader)
).isRequired,
onComplete: PropTypes.func.isRequired, onComplete: PropTypes.func.isRequired,
}; };

View File

@ -1,10 +1,4 @@
import React, { import React, { ReactElement, memo, useCallback, useEffect, useState } from 'react';
ReactElement,
memo,
useCallback,
useEffect,
useState,
} from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import DicomFileUploader, { import DicomFileUploader, {
DicomFileUploaderProgressEvent, DicomFileUploaderProgressEvent,
@ -21,9 +15,7 @@ type DicomUploadProgressItemProps = {
// eslint-disable-next-line react/display-name // eslint-disable-next-line react/display-name
const DicomUploadProgressItem = memo( const DicomUploadProgressItem = memo(
({ dicomFileUploader }: DicomUploadProgressItemProps): ReactElement => { ({ dicomFileUploader }: DicomUploadProgressItemProps): ReactElement => {
const [percentComplete, setPercentComplete] = useState( const [percentComplete, setPercentComplete] = useState(dicomFileUploader.getPercentComplete());
dicomFileUploader.getPercentComplete()
);
const [failedReason, setFailedReason] = useState(''); const [failedReason, setFailedReason] = useState('');
const [status, setStatus] = useState(dicomFileUploader.getStatus()); const [status, setStatus] = useState(dicomFileUploader.getStatus());
@ -62,7 +54,10 @@ const DicomUploadProgressItem = memo(
switch (dicomFileUploader.getStatus()) { switch (dicomFileUploader.getStatus()) {
case UploadStatus.Success: case UploadStatus.Success:
return ( return (
<Icon name="status-tracked" className="text-primary-light"></Icon> <Icon
name="status-tracked"
className="text-primary-light"
></Icon>
); );
case UploadStatus.InProgress: case UploadStatus.InProgress:
return <Icon name="icon-transferring"></Icon>; return <Icon name="icon-transferring"></Icon>;
@ -76,27 +71,25 @@ const DicomUploadProgressItem = memo(
}; };
return ( return (
<div className="flex w-full p-2.5 text-lg min-h-14 items-center border-b border-secondary-light overflow-hidden"> <div className="min-h-14 border-secondary-light flex w-full items-center overflow-hidden border-b p-2.5 text-lg">
<div className="flex flex-col gap-1 self-top w-0 grow shrink"> <div className="self-top flex w-0 shrink grow flex-col gap-1">
<div className="flex gap-4"> <div className="flex gap-4">
<div className="flex w-6 justify-center items-center shrink-0"> <div className="flex w-6 shrink-0 items-center justify-center">{getStatusIcon()}</div>
{getStatusIcon()} <div className="overflow-hidden text-ellipsis whitespace-nowrap">
</div>
<div className="text-ellipsis whitespace-nowrap overflow-hidden">
{dicomFileUploader.getFileName()} {dicomFileUploader.getFileName()}
</div> </div>
</div> </div>
{failedReason && <div className="pl-10">{failedReason}</div>} {failedReason && <div className="pl-10">{failedReason}</div>}
</div> </div>
<div className="w-24 flex items-center"> <div className="flex w-24 items-center">
{!isComplete() && ( {!isComplete() && (
<> <>
{dicomFileUploader.getStatus() === UploadStatus.InProgress && ( {dicomFileUploader.getStatus() === UploadStatus.InProgress && (
<div className="w-10 text-right">{percentComplete}%</div> <div className="w-10 text-right">{percentComplete}%</div>
)} )}
<div className="flex cursor-pointer ml-auto"> <div className="ml-auto flex cursor-pointer">
<Icon <Icon
className="self-center text-primary-active" className="text-primary-active self-center"
name="close" name="close"
onClick={cancelUpload} onClick={cancelUpload}
/> />

View File

@ -31,9 +31,7 @@ import { measurementMappingUtils } from './utils/measurementServiceMappings';
import type { PublicViewportOptions } from './services/ViewportService/Viewport'; import type { PublicViewportOptions } from './services/ViewportService/Viewport';
const Component = React.lazy(() => { const Component = React.lazy(() => {
return import( return import(/* webpackPrefetch: true */ './Viewport/OHIFCornerstoneViewport');
/* webpackPrefetch: true */ './Viewport/OHIFCornerstoneViewport'
);
}); });
const OHIFCornerstoneViewport = props => { const OHIFCornerstoneViewport = props => {
@ -69,9 +67,7 @@ const cornerstoneExtension: Types.Extensions.Extension = {
* *
* @param configuration.csToolsConfig - Passed directly to `initCornerstoneTools` * @param configuration.csToolsConfig - Passed directly to `initCornerstoneTools`
*/ */
preRegistration: function ( preRegistration: function (props: Types.Extensions.ExtensionParams): Promise<void> {
props: Types.Extensions.ExtensionParams
): Promise<void> {
const { servicesManager } = props; const { servicesManager } = props;
servicesManager.registerService(CornerstoneViewportService.REGISTRATION); servicesManager.registerService(CornerstoneViewportService.REGISTRATION);
servicesManager.registerService(ToolGroupService.REGISTRATION); servicesManager.registerService(ToolGroupService.REGISTRATION);
@ -139,5 +135,10 @@ const cornerstoneExtension: Types.Extensions.Extension = {
}; };
export type { PublicViewportOptions }; export type { PublicViewportOptions };
export { measurementMappingUtils, CornerstoneExtensionTypes, toolNames , getActiveViewportEnabledElement}; export {
measurementMappingUtils,
CornerstoneExtensionTypes,
toolNames,
getActiveViewportEnabledElement,
};
export default cornerstoneExtension; export default cornerstoneExtension;

View File

@ -49,8 +49,7 @@ export default async function init({
...cornerstone.getConfiguration(), ...cornerstone.getConfiguration(),
rendering: { rendering: {
...cornerstone.getConfiguration().rendering, ...cornerstone.getConfiguration().rendering,
strictZSpacingForVolumeViewport: strictZSpacingForVolumeViewport: appConfig.strictZSpacingForVolumeViewport,
appConfig.strictZSpacingForVolumeViewport,
}, },
}); });
@ -62,10 +61,7 @@ export default async function init({
initCornerstoneTools(); initCornerstoneTools();
Settings.getRuntimeSettings().set( Settings.getRuntimeSettings().set('useCursors', Boolean(appConfig.useCursors));
'useCursors',
Boolean(appConfig.useCursors)
);
const { const {
userAuthenticationService, userAuthenticationService,
@ -84,22 +80,15 @@ export default async function init({
window.extensionManager = extensionManager; window.extensionManager = extensionManager;
window.commandsManager = commandsManager; window.commandsManager = commandsManager;
if ( if (appConfig.showWarningMessageForCrossOrigin && !window.crossOriginIsolated) {
appConfig.showWarningMessageForCrossOrigin &&
!window.crossOriginIsolated
) {
uiNotificationService.show({ uiNotificationService.show({
title: 'Cross Origin Isolation', title: 'Cross Origin Isolation',
message: message: 'Cross Origin Isolation is not enabled, volume rendering will not work (e.g., MPR)',
'Cross Origin Isolation is not enabled, volume rendering will not work (e.g., MPR)',
type: 'warning', type: 'warning',
}); });
} }
if ( if (appConfig.showCPUFallbackMessage && cornerstone.getShouldUseCPURendering()) {
appConfig.showCPUFallbackMessage &&
cornerstone.getShouldUseCPURendering()
) {
_showCPURenderingModal(uiModalService, hangingProtocolService); _showCPURenderingModal(uiModalService, hangingProtocolService);
} }
@ -119,18 +108,14 @@ export default async function init({
clearOnModeExit: true, clearOnModeExit: true,
}); });
const labelmapRepresentation = const labelmapRepresentation = cornerstoneTools.Enums.SegmentationRepresentations.Labelmap;
cornerstoneTools.Enums.SegmentationRepresentations.Labelmap;
cornerstoneTools.segmentation.config.setGlobalRepresentationConfig( cornerstoneTools.segmentation.config.setGlobalRepresentationConfig(labelmapRepresentation, {
labelmapRepresentation,
{
fillAlpha: 0.3, fillAlpha: 0.3,
fillAlphaInactive: 0.2, fillAlphaInactive: 0.2,
outlineOpacity: 1, outlineOpacity: 1,
outlineOpacityInactive: 0.65, outlineOpacityInactive: 0.65,
} });
);
const metadataProvider = OHIF.classes.MetadataProvider; const metadataProvider = OHIF.classes.MetadataProvider;
@ -139,14 +124,8 @@ export default async function init({
cornerstoneStreamingImageVolumeLoader cornerstoneStreamingImageVolumeLoader
); );
hangingProtocolService.registerImageLoadStrategy( hangingProtocolService.registerImageLoadStrategy('interleaveCenter', interleaveCenterLoader);
'interleaveCenter', hangingProtocolService.registerImageLoadStrategy('interleaveTopToBottom', interleaveTopToBottom);
interleaveCenterLoader
);
hangingProtocolService.registerImageLoadStrategy(
'interleaveTopToBottom',
interleaveTopToBottom
);
hangingProtocolService.registerImageLoadStrategy('nth', nthLoader); hangingProtocolService.registerImageLoadStrategy('nth', nthLoader);
// add metadata providers // add metadata providers
@ -166,9 +145,7 @@ export default async function init({
initWADOImageLoader(userAuthenticationService, appConfig, extensionManager); initWADOImageLoader(userAuthenticationService, appConfig, extensionManager);
/* Measurement Service */ /* Measurement Service */
this.measurementServiceSource = connectToolsToMeasurementService( this.measurementServiceSource = connectToolsToMeasurementService(servicesManager);
servicesManager
);
initCineService(cineService); initCineService(cineService);
@ -178,31 +155,18 @@ export default async function init({
volumeInputArrayMap => { volumeInputArrayMap => {
for (const entry of volumeInputArrayMap.entries()) { for (const entry of volumeInputArrayMap.entries()) {
const [viewportId, volumeInputArray] = entry; const [viewportId, volumeInputArray] = entry;
const viewport = cornerstoneViewportService.getCornerstoneViewport( const viewport = cornerstoneViewportService.getCornerstoneViewport(viewportId);
viewportId
);
const ohifViewport = cornerstoneViewportService.getViewportInfo( const ohifViewport = cornerstoneViewportService.getViewportInfo(viewportId);
viewportId
);
const { const { lutPresentationStore, positionPresentationStore } = stateSyncService.getState();
lutPresentationStore,
positionPresentationStore,
} = stateSyncService.getState();
const { presentationIds } = ohifViewport.getViewportOptions(); const { presentationIds } = ohifViewport.getViewportOptions();
const presentations = { const presentations = {
positionPresentation: positionPresentation: positionPresentationStore[presentationIds?.positionPresentationId],
positionPresentationStore[presentationIds?.positionPresentationId], lutPresentation: lutPresentationStore[presentationIds?.lutPresentationId],
lutPresentation:
lutPresentationStore[presentationIds?.lutPresentationId],
}; };
cornerstoneViewportService.setVolumesForViewport( cornerstoneViewportService.setVolumesForViewport(viewport, volumeInputArray, presentations);
viewport,
volumeInputArray,
presentations
);
} }
} }
); );
@ -225,9 +189,7 @@ export default async function init({
const resetCrosshairs = evt => { const resetCrosshairs = evt => {
const { element } = evt.detail; const { element } = evt.detail;
const { viewportId, renderingEngineId } = cornerstone.getEnabledElement( const { viewportId, renderingEngineId } = cornerstone.getEnabledElement(element);
element
);
const toolGroup = cornerstoneTools.ToolGroupManager.getToolGroupForViewport( const toolGroup = cornerstoneTools.ToolGroupManager.getToolGroupForViewport(
viewportId, viewportId,
@ -253,10 +215,7 @@ export default async function init({
const { element } = evt.detail; const { element } = evt.detail;
element.addEventListener(EVENTS.CAMERA_RESET, resetCrosshairs); element.addEventListener(EVENTS.CAMERA_RESET, resetCrosshairs);
eventTarget.addEventListener( eventTarget.addEventListener(EVENTS.STACK_VIEWPORT_NEW_STACK, newStackCallback);
EVENTS.STACK_VIEWPORT_NEW_STACK,
newStackCallback
);
} }
function elementDisabledHandler(evt) { function elementDisabledHandler(evt) {
@ -271,15 +230,9 @@ export default async function init({
// ); // );
} }
eventTarget.addEventListener( eventTarget.addEventListener(EVENTS.ELEMENT_ENABLED, elementEnabledHandler.bind(null));
EVENTS.ELEMENT_ENABLED,
elementEnabledHandler.bind(null)
);
eventTarget.addEventListener( eventTarget.addEventListener(EVENTS.ELEMENT_DISABLED, elementDisabledHandler.bind(null));
EVENTS.ELEMENT_DISABLED,
elementDisabledHandler.bind(null)
);
viewportGridService.subscribe( viewportGridService.subscribe(
viewportGridService.EVENTS.ACTIVE_VIEWPORT_ID_CHANGED, viewportGridService.EVENTS.ACTIVE_VIEWPORT_ID_CHANGED,
@ -292,8 +245,7 @@ export default async function init({
// check if reference lines are active // check if reference lines are active
const referenceLinesEnabled = const referenceLinesEnabled =
toolGroup._toolInstances['ReferenceLines'].mode === toolGroup._toolInstances['ReferenceLines'].mode === Enums.ToolModes.Enabled;
Enums.ToolModes.Enabled;
if (!referenceLinesEnabled) { if (!referenceLinesEnabled) {
return; return;
@ -318,10 +270,9 @@ function CPUModal() {
return ( return (
<div> <div>
<p> <p>
Your computer does not have enough GPU power to support the default GPU Your computer does not have enough GPU power to support the default GPU rendering mode. OHIF
rendering mode. OHIF has switched to CPU rendering mode. Please note has switched to CPU rendering mode. Please note that CPU rendering does not support all
that CPU rendering does not support all features such as Volume features such as Volume Rendering, Multiplanar Reconstruction, and Segmentation Overlays.
Rendering, Multiplanar Reconstruction, and Segmentation Overlays.
</p> </p>
</div> </div>
); );

View File

@ -60,8 +60,7 @@ function initContextMenu({
*/ */
const cornerstoneViewportHandleEvent = (name, evt) => { const cornerstoneViewportHandleEvent = (name, evt) => {
const customizations = const customizations =
customizationService.get('cornerstoneViewportClickCommands') || customizationService.get('cornerstoneViewportClickCommands') || DEFAULT_CONTEXT_MENU_CLICKS;
DEFAULT_CONTEXT_MENU_CLICKS;
const toRun = customizations[name]; const toRun = customizations[name];
const options = { const options = {
nearbyToolData: findNearbyToolData(commandsManager, evt), nearbyToolData: findNearbyToolData(commandsManager, evt),
@ -84,30 +83,18 @@ function initContextMenu({
// TODO check update upstream // TODO check update upstream
setEnabledElement(viewportId, element); setEnabledElement(viewportId, element);
element.addEventListener( element.addEventListener(cs3DToolsEvents.MOUSE_CLICK, cornerstoneViewportHandleClick);
cs3DToolsEvents.MOUSE_CLICK,
cornerstoneViewportHandleClick
);
} }
function elementDisabledHandler(evt) { function elementDisabledHandler(evt) {
const { element } = evt.detail; const { element } = evt.detail;
element.removeEventListener( element.removeEventListener(cs3DToolsEvents.MOUSE_CLICK, cornerstoneViewportHandleClick);
cs3DToolsEvents.MOUSE_CLICK,
cornerstoneViewportHandleClick
);
} }
eventTarget.addEventListener( eventTarget.addEventListener(EVENTS.ELEMENT_ENABLED, elementEnabledHandler.bind(null));
EVENTS.ELEMENT_ENABLED,
elementEnabledHandler.bind(null)
);
eventTarget.addEventListener( eventTarget.addEventListener(EVENTS.ELEMENT_DISABLED, elementDisabledHandler.bind(null));
EVENTS.ELEMENT_DISABLED,
elementDisabledHandler.bind(null)
);
} }
export default initContextMenu; export default initContextMenu;

View File

@ -39,10 +39,7 @@ export type initDoubleClickArgs = {
commandsManager: CommandsManager; commandsManager: CommandsManager;
}; };
function initDoubleClick({ function initDoubleClick({ customizationService, commandsManager }: initDoubleClickArgs): void {
customizationService,
commandsManager,
}: initDoubleClickArgs): void {
const cornerstoneViewportHandleDoubleClick = (evt: CustomEvent) => { const cornerstoneViewportHandleDoubleClick = (evt: CustomEvent) => {
// Do not allow double click on a tool. // Do not allow double click on a tool.
const nearbyToolData = findNearbyToolData(commandsManager, evt); const nearbyToolData = findNearbyToolData(commandsManager, evt);
@ -54,8 +51,7 @@ function initDoubleClick({
// Allows for the customization of the double click on a viewport. // Allows for the customization of the double click on a viewport.
const customizations = const customizations =
customizationService.get('cornerstoneViewportClickCommands') || customizationService.get('cornerstoneViewportClickCommands') || DEFAULT_DOUBLE_CLICK;
DEFAULT_DOUBLE_CLICK;
const toRun = customizations[eventName]; const toRun = customizations[eventName];
@ -84,15 +80,9 @@ function initDoubleClick({
); );
} }
eventTarget.addEventListener( eventTarget.addEventListener(EVENTS.ELEMENT_ENABLED, elementEnabledHandler.bind(null));
EVENTS.ELEMENT_ENABLED,
elementEnabledHandler.bind(null)
);
eventTarget.addEventListener( eventTarget.addEventListener(EVENTS.ELEMENT_DISABLED, elementDisabledHandler.bind(null));
EVENTS.ELEMENT_DISABLED,
elementDisabledHandler.bind(null)
);
} }
export default initDoubleClick; export default initDoubleClick;

View File

@ -127,11 +127,8 @@ const initMeasurementService = (
}; };
const connectToolsToMeasurementService = servicesManager => { const connectToolsToMeasurementService = servicesManager => {
const { const { measurementService, displaySetService, cornerstoneViewportService } =
measurementService, servicesManager.services;
displaySetService,
cornerstoneViewportService,
} = servicesManager.services;
const csTools3DVer1MeasurementSource = initMeasurementService( const csTools3DVer1MeasurementSource = initMeasurementService(
measurementService, measurementService,
displaySetService, displaySetService,
@ -153,10 +150,7 @@ const connectToolsToMeasurementService = servicesManager => {
} = annotationAddedEventDetail; } = annotationAddedEventDetail;
const { toolName } = metadata; const { toolName } = metadata;
if ( if (csToolsEvent.type === completedEvt && toolName === toolNames.CalibrationLine) {
csToolsEvent.type === completedEvt &&
toolName === toolNames.CalibrationLine
) {
// show modal to input the measurement (mm) // show modal to input the measurement (mm)
onCompletedCalibrationLine(servicesManager, csToolsEvent) onCompletedCalibrationLine(servicesManager, csToolsEvent)
.then( .then(
@ -212,10 +206,8 @@ const connectToolsToMeasurementService = servicesManager => {
try { try {
const annotationSelectionEventDetail = csToolsEvent.detail; const annotationSelectionEventDetail = csToolsEvent.detail;
const { const { added: addedSelectedAnnotationUIDs, removed: removedSelectedAnnotationUIDs } =
added: addedSelectedAnnotationUIDs, annotationSelectionEventDetail;
removed: removedSelectedAnnotationUIDs,
} = annotationSelectionEventDetail;
if (removedSelectedAnnotationUIDs) { if (removedSelectedAnnotationUIDs) {
removedSelectedAnnotationUIDs.forEach(annotationUID => removedSelectedAnnotationUIDs.forEach(annotationUID =>
@ -283,12 +275,8 @@ const connectMeasurementServiceToTools = (
cornerstoneViewportService, cornerstoneViewportService,
measurementSource measurementSource
) => { ) => {
const { const { MEASUREMENT_REMOVED, MEASUREMENTS_CLEARED, MEASUREMENT_UPDATED, RAW_MEASUREMENT_ADDED } =
MEASUREMENT_REMOVED, measurementService.EVENTS;
MEASUREMENTS_CLEARED,
MEASUREMENT_UPDATED,
RAW_MEASUREMENT_ADDED,
} = measurementService.EVENTS;
const csTools3DVer1MeasurementSource = measurementService.getSource( const csTools3DVer1MeasurementSource = measurementService.getSource(
CORNERSTONE_3D_TOOLS_SOURCE_NAME, CORNERSTONE_3D_TOOLS_SOURCE_NAME,
@ -351,11 +339,7 @@ const connectMeasurementServiceToTools = (
return; return;
} }
const { const { referenceSeriesUID, referenceStudyUID, SOPInstanceUID } = measurement;
referenceSeriesUID,
referenceStudyUID,
SOPInstanceUID,
} = measurement;
const instance = DicomMetadataStore.getInstance( const instance = DicomMetadataStore.getInstance(
referenceStudyUID, referenceStudyUID,
@ -368,9 +352,7 @@ const connectMeasurementServiceToTools = (
if (measurement?.metadata?.referencedImageId) { if (measurement?.metadata?.referencedImageId) {
imageId = measurement.metadata.referencedImageId; imageId = measurement.metadata.referencedImageId;
frameNumber = getSOPInstanceAttributes( frameNumber = getSOPInstanceAttributes(measurement.metadata.referencedImageId).frameNumber;
measurement.metadata.referencedImageId
).frameNumber;
} else { } else {
imageId = dataSource.getImageIdsForInstance({ instance }); imageId = dataSource.getImageIdsForInstance({ instance });
} }

View File

@ -1,9 +1,7 @@
import * as cornerstone from '@cornerstonejs/core'; import * as cornerstone from '@cornerstonejs/core';
import { volumeLoader } from '@cornerstonejs/core'; import { volumeLoader } from '@cornerstonejs/core';
import { cornerstoneStreamingImageVolumeLoader } from '@cornerstonejs/streaming-image-volume-loader'; import { cornerstoneStreamingImageVolumeLoader } from '@cornerstonejs/streaming-image-volume-loader';
import dicomImageLoader, { import dicomImageLoader, { webWorkerManager } from '@cornerstonejs/dicom-image-loader';
webWorkerManager,
} from '@cornerstonejs/dicom-image-loader';
import dicomParser from 'dicom-parser'; import dicomParser from 'dicom-parser';
import { errorHandler, utils } from '@ohif/core'; import { errorHandler, utils } from '@ohif/core';
@ -41,10 +39,7 @@ export default function initWADOImageLoader(
dicomImageLoader.external.cornerstone = cornerstone; dicomImageLoader.external.cornerstone = cornerstone;
dicomImageLoader.external.dicomParser = dicomParser; dicomImageLoader.external.dicomParser = dicomParser;
registerVolumeLoader( registerVolumeLoader('cornerstoneStreamingImageVolume', cornerstoneStreamingImageVolumeLoader);
'cornerstoneStreamingImageVolume',
cornerstoneStreamingImageVolumeLoader
);
dicomImageLoader.configure({ dicomImageLoader.configure({
decodeConfig: { decodeConfig: {
@ -55,10 +50,9 @@ export default function initWADOImageLoader(
// we should set this flag to false. // we should set this flag to false.
convertFloatPixelDataToInt: false, convertFloatPixelDataToInt: false,
}, },
beforeSend: function(xhr) { beforeSend: function (xhr) {
//TODO should be removed in the future and request emitted by DicomWebDataSource //TODO should be removed in the future and request emitted by DicomWebDataSource
const sourceConfig = const sourceConfig = extensionManager.getActiveDataSource()?.[0].getConfig() ?? {};
extensionManager.getActiveDataSource()?.[0].getConfig() ?? {};
const headers = userAuthenticationService.getAuthorizationHeader(); const headers = userAuthenticationService.getAuthorizationHeader();
const acceptHeader = utils.generateAcceptHeader( const acceptHeader = utils.generateAcceptHeader(
sourceConfig.acceptHeader, sourceConfig.acceptHeader,

View File

@ -1,16 +1,8 @@
import { ServicesManager, Types } from '@ohif/core'; import { ServicesManager, Types } from '@ohif/core';
import { import { cache as cs3DCache, Enums, volumeLoader, utilities as utils } from '@cornerstonejs/core';
cache as cs3DCache,
Enums,
volumeLoader,
utilities as utils,
} from '@cornerstonejs/core';
import getCornerstoneViewportType from '../../utils/getCornerstoneViewportType'; import getCornerstoneViewportType from '../../utils/getCornerstoneViewportType';
import { import { StackViewportData, VolumeViewportData } from '../../types/CornerstoneCacheService';
StackViewportData,
VolumeViewportData,
} from '../../types/CornerstoneCacheService';
const VOLUME_LOADER_SCHEME = 'cornerstoneStreamingImageVolume'; const VOLUME_LOADER_SCHEME = 'cornerstoneStreamingImageVolume';
@ -18,9 +10,7 @@ class CornerstoneCacheService {
static REGISTRATION = { static REGISTRATION = {
name: 'cornerstoneCacheService', name: 'cornerstoneCacheService',
altName: 'CornerstoneCacheService', altName: 'CornerstoneCacheService',
create: ({ create: ({ servicesManager }: Types.Extensions.ExtensionParams): CornerstoneCacheService => {
servicesManager,
}: Types.Extensions.ExtensionParams): CornerstoneCacheService => {
return new CornerstoneCacheService(servicesManager); return new CornerstoneCacheService(servicesManager);
}, },
}; };
@ -77,11 +67,7 @@ class CornerstoneCacheService {
cs3DViewportType === Enums.ViewportType.ORTHOGRAPHIC || cs3DViewportType === Enums.ViewportType.ORTHOGRAPHIC ||
cs3DViewportType === Enums.ViewportType.VOLUME_3D cs3DViewportType === Enums.ViewportType.VOLUME_3D
) { ) {
viewportData = await this._getVolumeViewportData( viewportData = await this._getVolumeViewportData(dataSource, displaySets, cs3DViewportType);
dataSource,
displaySets,
cs3DViewportType
);
} }
viewportData.viewportType = cs3DViewportType; viewportData.viewportType = cs3DViewportType;
@ -134,20 +120,14 @@ class CornerstoneCacheService {
// For Stack Viewport we don't have fusion currently // For Stack Viewport we don't have fusion currently
const displaySet = displaySets[0]; const displaySet = displaySets[0];
let stackImageIds = this.stackImageIds.get( let stackImageIds = this.stackImageIds.get(displaySet.displaySetInstanceUID);
displaySet.displaySetInstanceUID
);
if (!stackImageIds) { if (!stackImageIds) {
stackImageIds = this._getCornerstoneStackImageIds(displaySet, dataSource); stackImageIds = this._getCornerstoneStackImageIds(displaySet, dataSource);
this.stackImageIds.set(displaySet.displaySetInstanceUID, stackImageIds); this.stackImageIds.set(displaySet.displaySetInstanceUID, stackImageIds);
} }
const { const { displaySetInstanceUID, StudyInstanceUID, isCompositeStack } = displaySet;
displaySetInstanceUID,
StudyInstanceUID,
isCompositeStack,
} = displaySet;
const StackViewportData: StackViewportData = { const StackViewportData: StackViewportData = {
viewportType, viewportType,
@ -196,31 +176,22 @@ class CornerstoneCacheService {
continue; continue;
} }
const volumeLoaderSchema = const volumeLoaderSchema = displaySet.volumeLoaderSchema ?? VOLUME_LOADER_SCHEME;
displaySet.volumeLoaderSchema ?? VOLUME_LOADER_SCHEME;
const volumeId = `${volumeLoaderSchema}:${displaySet.displaySetInstanceUID}`; const volumeId = `${volumeLoaderSchema}:${displaySet.displaySetInstanceUID}`;
let volumeImageIds = this.volumeImageIds.get( let volumeImageIds = this.volumeImageIds.get(displaySet.displaySetInstanceUID);
displaySet.displaySetInstanceUID
);
let volume = cs3DCache.getVolume(volumeId); let volume = cs3DCache.getVolume(volumeId);
if (!volumeImageIds || !volume) { if (!volumeImageIds || !volume) {
volumeImageIds = this._getCornerstoneVolumeImageIds( volumeImageIds = this._getCornerstoneVolumeImageIds(displaySet, dataSource);
displaySet,
dataSource
);
volume = await volumeLoader.createAndCacheVolume(volumeId, { volume = await volumeLoader.createAndCacheVolume(volumeId, {
imageIds: volumeImageIds, imageIds: volumeImageIds,
}); });
this.volumeImageIds.set( this.volumeImageIds.set(displaySet.displaySetInstanceUID, volumeImageIds);
displaySet.displaySetInstanceUID,
volumeImageIds
);
} }
volumeData.push({ volumeData.push({
@ -268,10 +239,7 @@ class CornerstoneCacheService {
} }
private _getCornerstoneVolumeImageIds(displaySet, dataSource): string[] { private _getCornerstoneVolumeImageIds(displaySet, dataSource): string[] {
const stackImageIds = this._getCornerstoneStackImageIds( const stackImageIds = this._getCornerstoneStackImageIds(displaySet, dataSource);
displaySet,
dataSource
);
return stackImageIds; return stackImageIds;
} }

View File

@ -7,12 +7,8 @@
* @returns An array of object that includes data, id, segmentIndex, color * @returns An array of object that includes data, id, segmentIndex, color
* and geometry Id * and geometry Id
*/ */
export function mapROIContoursToRTStructData( export function mapROIContoursToRTStructData(structureSet: unknown, rtDisplaySetUID: unknown) {
structureSet: unknown, return structureSet.ROIContours.map(({ contourPoints, ROINumber, ROIName, colorArray }) => {
rtDisplaySetUID: unknown
) {
return structureSet.ROIContours.map(
({ contourPoints, ROINumber, ROIName, colorArray }) => {
const data = contourPoints.map(({ points, ...rest }) => { const data = contourPoints.map(({ points, ...rest }) => {
const newPoints = points.map(({ x, y, z }) => { const newPoints = points.map(({ x, y, z }) => {
return [x, y, z]; return [x, y, z];
@ -33,6 +29,5 @@ export function mapROIContoursToRTStructData(
color: colorArray, color: colorArray,
geometryId: `${rtDisplaySetUID}:${id}:segmentIndex-${ROINumber}`, geometryId: `${rtDisplaySetUID}:${id}:segmentIndex-${ROINumber}`,
}; };
} });
);
} }

View File

@ -21,11 +21,7 @@ import {
import isEqual from 'lodash.isequal'; import isEqual from 'lodash.isequal';
import { Types as ohifTypes } from '@ohif/core'; import { Types as ohifTypes } from '@ohif/core';
import { easeInOutBell, reverseEaseInOutBell } from '../../utils/transitions'; import { easeInOutBell, reverseEaseInOutBell } from '../../utils/transitions';
import { import { Segment, Segmentation, SegmentationConfig } from './SegmentationServiceTypes';
Segment,
Segmentation,
SegmentationConfig,
} from './SegmentationServiceTypes';
import { mapROIContoursToRTStructData } from './RTSTRUCT/mapROIContoursToRTStructData'; import { mapROIContoursToRTStructData } from './RTSTRUCT/mapROIContoursToRTStructData';
const { COLOR_LUT } = cstConstants; const { COLOR_LUT } = cstConstants;
@ -42,8 +38,7 @@ const EVENTS = {
// fired when the segmentation is removed // fired when the segmentation is removed
SEGMENTATION_REMOVED: 'event::segmentation_removed', SEGMENTATION_REMOVED: 'event::segmentation_removed',
// fired when the configuration for the segmentation is changed (e.g., brush size, render fill, outline thickness, etc.) // fired when the configuration for the segmentation is changed (e.g., brush size, render fill, outline thickness, etc.)
SEGMENTATION_CONFIGURATION_CHANGED: SEGMENTATION_CONFIGURATION_CHANGED: 'event::segmentation_configuration_changed',
'event::segmentation_configuration_changed',
// fired when the active segment is loaded in SEG or RTSTRUCT // fired when the active segment is loaded in SEG or RTSTRUCT
SEGMENT_LOADING_COMPLETE: 'event::segment_loading_complete', SEGMENT_LOADING_COMPLETE: 'event::segment_loading_complete',
// for all segments // for all segments
@ -64,9 +59,7 @@ class SegmentationService extends PubSubService {
static REGISTRATION = { static REGISTRATION = {
name: 'segmentationService', name: 'segmentationService',
altName: 'SegmentationService', altName: 'SegmentationService',
create: ({ create: ({ servicesManager }: OhifTypes.Extensions.ExtensionParams): SegmentationService => {
servicesManager,
}: OhifTypes.Extensions.ExtensionParams): SegmentationService => {
return new SegmentationService({ servicesManager }); return new SegmentationService({ servicesManager });
}, },
}; };
@ -138,10 +131,10 @@ class SegmentationService extends PubSubService {
toolGroupId = toolGroupId ?? this._getFirstToolGroupId(); toolGroupId = toolGroupId ?? this._getFirstToolGroupId();
const { const { segmentationRepresentationUID, segmentation } = this._getSegmentationInfo(
segmentationRepresentationUID, segmentationId,
segmentation, toolGroupId
} = this._getSegmentationInfo(segmentationId, toolGroupId); );
if (this._getSegmentInfo(segmentation, segmentIndex)) { if (this._getSegmentInfo(segmentation, segmentIndex)) {
throw new Error(`Segment ${segmentIndex} already exists`); throw new Error(`Segment ${segmentIndex} already exists`);
@ -166,32 +159,14 @@ class SegmentationService extends PubSubService {
const suppressEvents = true; const suppressEvents = true;
if (properties !== undefined) { if (properties !== undefined) {
const { const { color: newColor, opacity, isLocked, visibility, active } = properties;
color: newColor,
opacity,
isLocked,
visibility,
active,
} = properties;
if (newColor !== undefined) { if (newColor !== undefined) {
this._setSegmentColor( this._setSegmentColor(segmentationId, segmentIndex, newColor, toolGroupId, suppressEvents);
segmentationId,
segmentIndex,
newColor,
toolGroupId,
suppressEvents
);
} }
if (opacity !== undefined) { if (opacity !== undefined) {
this._setSegmentOpacity( this._setSegmentOpacity(segmentationId, segmentIndex, opacity, toolGroupId, suppressEvents);
segmentationId,
segmentIndex,
opacity,
toolGroupId,
suppressEvents
);
} }
if (visibility !== undefined) { if (visibility !== undefined) {
@ -209,12 +184,7 @@ class SegmentationService extends PubSubService {
} }
if (isLocked !== undefined) { if (isLocked !== undefined) {
this._setSegmentLocked( this._setSegmentLocked(segmentationId, segmentIndex, isLocked, suppressEvents);
segmentationId,
segmentIndex,
isLocked,
suppressEvents
);
} }
} }
@ -285,9 +255,7 @@ class SegmentationService extends PubSubService {
if (segmentation.activeSegmentIndex === segmentIndex) { if (segmentation.activeSegmentIndex === segmentIndex) {
const segmentIndices = Object.keys(segmentation.segments); const segmentIndices = Object.keys(segmentation.segments);
const newActiveSegmentIndex = segmentIndices.length const newActiveSegmentIndex = segmentIndices.length ? Number(segmentIndices[0]) : 1;
? Number(segmentIndices[0])
: 1;
this._setActiveSegment(segmentationId, newActiveSegmentIndex, true); this._setActiveSegment(segmentationId, newActiveSegmentIndex, true);
} }
@ -319,19 +287,10 @@ class SegmentationService extends PubSubService {
isLocked: boolean isLocked: boolean
): void { ): void {
const suppressEvents = false; const suppressEvents = false;
this._setSegmentLocked( this._setSegmentLocked(segmentationId, segmentIndex, isLocked, suppressEvents);
segmentationId,
segmentIndex,
isLocked,
suppressEvents
);
} }
public setSegmentLabel( public setSegmentLabel(segmentationId: string, segmentIndex: number, segmentLabel: string): void {
segmentationId: string,
segmentIndex: number,
segmentLabel: string
): void {
this._setSegmentLabel(segmentationId, segmentIndex, segmentLabel); this._setSegmentLabel(segmentationId, segmentIndex, segmentLabel);
} }
@ -387,24 +346,14 @@ class SegmentationService extends PubSubService {
this._setSegmentOpacity(segmentationId, segmentIndex, opacity, toolGroupId); this._setSegmentOpacity(segmentationId, segmentIndex, opacity, toolGroupId);
} }
public setActiveSegmentationForToolGroup( public setActiveSegmentationForToolGroup(segmentationId: string, toolGroupId?: string): void {
segmentationId: string,
toolGroupId?: string
): void {
toolGroupId = toolGroupId ?? this._getFirstToolGroupId(); toolGroupId = toolGroupId ?? this._getFirstToolGroupId();
const suppressEvents = false; const suppressEvents = false;
this._setActiveSegmentationForToolGroup( this._setActiveSegmentationForToolGroup(segmentationId, toolGroupId, suppressEvents);
segmentationId,
toolGroupId,
suppressEvents
);
} }
public setActiveSegmentForSegmentation( public setActiveSegmentForSegmentation(segmentationId: string, segmentIndex: number): void {
segmentationId: string,
segmentIndex: number
): void {
this._setActiveSegment(segmentationId, segmentIndex, false); this._setActiveSegment(segmentationId, segmentIndex, false);
} }
@ -420,9 +369,7 @@ class SegmentationService extends PubSubService {
* @return Array of segmentations * @return Array of segmentations
*/ */
public getSegmentations( public getSegmentations(filterNonHydratedSegmentations = true): Segmentation[] {
filterNonHydratedSegmentations = true
): Segmentation[] {
const segmentations = this._getSegmentations(); const segmentations = this._getSegmentations();
return ( return (
@ -435,10 +382,7 @@ class SegmentationService extends PubSubService {
private _getSegmentations(): Segmentation[] { private _getSegmentations(): Segmentation[] {
const segmentations = this.arrayOfObjects(this.segmentations); const segmentations = this.arrayOfObjects(this.segmentations);
return ( return segmentations && segmentations.map(m => this.segmentations[Object.keys(m)[0]]);
segmentations &&
segmentations.map(m => this.segmentations[Object.keys(m)[0]])
);
} }
/** /**
@ -477,8 +421,7 @@ class SegmentationService extends PubSubService {
} }
const representationType = segmentation.type; const representationType = segmentation.type;
const representationData = const representationData = segmentation.representationData[representationType];
segmentation.representationData[representationType];
cstSegmentation.addSegmentations([ cstSegmentation.addSegmentations([
{ {
segmentationId, segmentationId,
@ -566,9 +509,7 @@ class SegmentationService extends PubSubService {
const { labelmapBufferArray, referencedVolumeId } = segDisplaySet; const { labelmapBufferArray, referencedVolumeId } = segDisplaySet;
if (!labelmapBufferArray || !referencedVolumeId) { if (!labelmapBufferArray || !referencedVolumeId) {
throw new Error( throw new Error('No labelmapBufferArray or referencedVolumeId found for the SEG displaySet');
'No labelmapBufferArray or referencedVolumeId found for the SEG displaySet'
);
} }
// if the labelmap doesn't exist, we need to create it first from the // if the labelmap doesn't exist, we need to create it first from the
@ -576,23 +517,18 @@ class SegmentationService extends PubSubService {
const referencedVolume = cache.getVolume(referencedVolumeId); const referencedVolume = cache.getVolume(referencedVolumeId);
if (!referencedVolume) { if (!referencedVolume) {
throw new Error( throw new Error(`No volume found for referencedVolumeId: ${referencedVolumeId}`);
`No volume found for referencedVolumeId: ${referencedVolumeId}`
);
} }
// Force use of a Uint8Array SharedArrayBuffer for the segmentation to save space and so // Force use of a Uint8Array SharedArrayBuffer for the segmentation to save space and so
// it is easily compressible in worker thread. // it is easily compressible in worker thread.
const derivedVolume = await volumeLoader.createAndCacheDerivedVolume( const derivedVolume = await volumeLoader.createAndCacheDerivedVolume(referencedVolumeId, {
referencedVolumeId,
{
volumeId: segmentationId, volumeId: segmentationId,
targetBuffer: { targetBuffer: {
type: 'Uint8Array', type: 'Uint8Array',
sharedArrayBuffer: true, sharedArrayBuffer: true,
}, },
} });
);
const derivedVolumeScalarData = derivedVolume.getScalarData(); const derivedVolumeScalarData = derivedVolume.getScalarData();
const segmentsInfo = segDisplaySet.segMetadata.data; const segmentsInfo = segDisplaySet.segMetadata.data;
@ -680,10 +616,7 @@ class SegmentationService extends PubSubService {
const defaultScheme = this._getDefaultSegmentationScheme(); const defaultScheme = this._getDefaultSegmentationScheme();
const rtDisplaySetUID = rtDisplaySet.displaySetInstanceUID; const rtDisplaySetUID = rtDisplaySet.displaySetInstanceUID;
const allRTStructData = mapROIContoursToRTStructData( const allRTStructData = mapROIContoursToRTStructData(structureSet, rtDisplaySetUID);
structureSet,
rtDisplaySetUID
);
// sort by segmentIndex // sort by segmentIndex
allRTStructData.sort((a, b) => a.segmentIndex - b.segmentIndex); allRTStructData.sort((a, b) => a.segmentIndex - b.segmentIndex);
@ -726,9 +659,7 @@ class SegmentationService extends PubSubService {
// catch error instead of failing to allow loading to continue // catch error instead of failing to allow loading to continue
try { try {
const geometry = await geometryLoader.createAndCacheGeometry( const geometry = await geometryLoader.createAndCacheGeometry(geometryId, {
geometryId,
{
geometryData: { geometryData: {
data, data,
id, id,
@ -737,8 +668,7 @@ class SegmentationService extends PubSubService {
segmentIndex, segmentIndex,
}, },
type: csEnums.GeometryType.CONTOUR, type: csEnums.GeometryType.CONTOUR,
} });
);
const contourSet = geometry.data; const contourSet = geometry.data;
const centroid = contourSet.getCentroid(); const centroid = contourSet.getCentroid();
@ -758,9 +688,7 @@ class SegmentationService extends PubSubService {
const numInitialized = Object.keys(segmentsCachedStats).length; const numInitialized = Object.keys(segmentsCachedStats).length;
// Calculate percentage completed // Calculate percentage completed
const percentComplete = Math.round( const percentComplete = Math.round((numInitialized / allRTStructData.length) * 100);
(numInitialized / allRTStructData.length) * 100
);
this._broadcastEvent(EVENTS.SEGMENT_LOADING_COMPLETE, { this._broadcastEvent(EVENTS.SEGMENT_LOADING_COMPLETE, {
percentComplete, percentComplete,
@ -824,8 +752,7 @@ class SegmentationService extends PubSubService {
const { world } = center; const { world } = center;
// todo: generalize // todo: generalize
toolGroupId = toolGroupId = toolGroupId || this._getToolGroupIdsWithSegmentation(segmentationId);
toolGroupId || this._getToolGroupIdsWithSegmentation(segmentationId);
const toolGroups = []; const toolGroups = [];
@ -842,10 +769,7 @@ class SegmentationService extends PubSubService {
// @ts-ignore // @ts-ignore
for (const { viewportId, renderingEngineId } of viewportsInfo) { for (const { viewportId, renderingEngineId } of viewportsInfo) {
const { viewport } = getEnabledElementByIds( const { viewport } = getEnabledElementByIds(viewportId, renderingEngineId);
viewportId,
renderingEngineId
);
cstUtils.viewport.jumpToWorld(viewport, world); cstUtils.viewport.jumpToWorld(viewport, world);
} }
@ -888,9 +812,7 @@ class SegmentationService extends PubSubService {
const { segments } = segmentation; const { segments } = segmentation;
const highlightFn = const highlightFn =
type === LABELMAP type === LABELMAP ? this._highlightLabelmap.bind(this) : this._highlightContour.bind(this);
? this._highlightLabelmap.bind(this)
: this._highlightContour.bind(this);
const adjustedAlpha = type === LABELMAP ? alpha : 1 - alpha; const adjustedAlpha = type === LABELMAP ? alpha : 1 - alpha;
@ -914,9 +836,7 @@ class SegmentationService extends PubSubService {
): Promise<string> => { ): Promise<string> => {
const { displaySetService } = this.servicesManager.services; const { displaySetService } = this.servicesManager.services;
const displaySet = displaySetService.getDisplaySetByUID( const displaySet = displaySetService.getDisplaySetByUID(displaySetInstanceUID);
displaySetInstanceUID
);
// Todo: we currently only support labelmap for segmentation for a displaySet // Todo: we currently only support labelmap for segmentation for a displaySet
const representationType = LABELMAP; const representationType = LABELMAP;
@ -977,9 +897,7 @@ class SegmentationService extends PubSubService {
const segmentation = this.getSegmentation(segmentationId); const segmentation = this.getSegmentation(segmentationId);
if (!segmentation) { if (!segmentation) {
throw new Error( throw new Error(`Segmentation with segmentationId ${segmentationId} not found.`);
`Segmentation with segmentationId ${segmentationId} not found.`
);
} }
if (hydrateSegmentation) { if (hydrateSegmentation) {
@ -1019,34 +937,16 @@ class SegmentationService extends PubSubService {
continue; continue;
} }
const { const { segmentIndex, color, isLocked, isVisible: visibility, opacity } = segment;
segmentIndex,
color,
isLocked,
isVisible: visibility,
opacity,
} = segment;
const suppressEvents = true; const suppressEvents = true;
if (color !== undefined) { if (color !== undefined) {
this._setSegmentColor( this._setSegmentColor(segmentationId, segmentIndex, color, toolGroupId, suppressEvents);
segmentationId,
segmentIndex,
color,
toolGroupId,
suppressEvents
);
} }
if (opacity !== undefined) { if (opacity !== undefined) {
this._setSegmentOpacity( this._setSegmentOpacity(segmentationId, segmentIndex, opacity, toolGroupId, suppressEvents);
segmentationId,
segmentIndex,
opacity,
toolGroupId,
suppressEvents
);
} }
if (visibility !== undefined) { if (visibility !== undefined) {
@ -1060,12 +960,7 @@ class SegmentationService extends PubSubService {
} }
if (isLocked !== undefined) { if (isLocked !== undefined) {
this._setSegmentLocked( this._setSegmentLocked(segmentationId, segmentIndex, isLocked, suppressEvents);
segmentationId,
segmentIndex,
isLocked,
suppressEvents
);
} }
} }
}; };
@ -1102,25 +997,16 @@ class SegmentationService extends PubSubService {
}); });
}; };
public getToolGroupIdsWithSegmentation = ( public getToolGroupIdsWithSegmentation = (segmentationId: string): string[] => {
segmentationId: string const toolGroupIds = cstSegmentation.state.getToolGroupIdsWithSegmentation(segmentationId);
): string[] => {
const toolGroupIds = cstSegmentation.state.getToolGroupIdsWithSegmentation(
segmentationId
);
return toolGroupIds; return toolGroupIds;
}; };
public hydrateSegmentation = ( public hydrateSegmentation = (segmentationId: string, suppressEvents = false): void => {
segmentationId: string,
suppressEvents = false
): void => {
const segmentation = this.getSegmentation(segmentationId); const segmentation = this.getSegmentation(segmentationId);
if (!segmentation) { if (!segmentation) {
throw new Error( throw new Error(`Segmentation with segmentationId ${segmentationId} not found.`);
`Segmentation with segmentationId ${segmentationId} not found.`
);
} }
this._setDisplaySetIsHydrated(segmentationId, true); this._setDisplaySetIsHydrated(segmentationId, true);
@ -1134,13 +1020,8 @@ class SegmentationService extends PubSubService {
} }
}; };
private _setDisplaySetIsHydrated( private _setDisplaySetIsHydrated(displaySetUID: string, isHydrated: boolean): void {
displaySetUID: string, const { DisplaySetService: displaySetService } = this.servicesManager.services;
isHydrated: boolean
): void {
const {
DisplaySetService: displaySetService,
} = this.servicesManager.services;
const displaySet = displaySetService.getDisplaySetByUID(displaySetUID); const displaySet = displaySetService.getDisplaySetByUID(displaySetUID);
displaySet.isHydrated = isHydrated; displaySet.isHydrated = isHydrated;
displaySetService.setDisplaySetMetadataInvalidated(displaySetUID, false); displaySetService.setDisplaySetMetadataInvalidated(displaySetUID, false);
@ -1259,17 +1140,13 @@ class SegmentationService extends PubSubService {
): void { ): void {
const uids = segmentationRepresentationUIDsIds || []; const uids = segmentationRepresentationUIDsIds || [];
if (!uids.length) { if (!uids.length) {
const representations = cstSegmentation.state.getSegmentationRepresentations( const representations = cstSegmentation.state.getSegmentationRepresentations(toolGroupId);
toolGroupId
);
if (!representations || !representations.length) { if (!representations || !representations.length) {
return; return;
} }
uids.push( uids.push(...representations.map(rep => rep.segmentationRepresentationUID));
...representations.map(rep => rep.segmentationRepresentationUID)
);
} }
cstSegmentation.removeSegmentationsFromToolGroup(toolGroupId, uids); cstSegmentation.removeSegmentationsFromToolGroup(toolGroupId, uids);
@ -1285,9 +1162,7 @@ class SegmentationService extends PubSubService {
const wasActive = segmentation.isActive; const wasActive = segmentation.isActive;
if (!segmentationId || !segmentation) { if (!segmentationId || !segmentation) {
console.warn( console.warn(`No segmentationId provided, or unable to find segmentation by id.`);
`No segmentationId provided, or unable to find segmentation by id.`
);
return; return;
} }
@ -1309,11 +1184,7 @@ class SegmentationService extends PubSubService {
if (remainingSegmentations.length) { if (remainingSegmentations.length) {
const { id } = remainingSegmentations[0]; const { id } = remainingSegmentations[0];
this._setActiveSegmentationForToolGroup( this._setActiveSegmentationForToolGroup(id, this._getFirstToolGroupId(), false);
id,
this._getFirstToolGroupId(),
false
);
} }
} }
@ -1337,9 +1208,8 @@ class SegmentationService extends PubSubService {
// toolGroupId // toolGroupId
// ); // );
const segmentationRepresentations = this.getSegmentationRepresentationsForToolGroup( const segmentationRepresentations =
toolGroupId this.getSegmentationRepresentationsForToolGroup(toolGroupId);
);
const typeToUse = segmentationRepresentations?.[0]?.type || LABELMAP; const typeToUse = segmentationRepresentations?.[0]?.type || LABELMAP;
@ -1397,11 +1267,7 @@ class SegmentationService extends PubSubService {
setConfigValueIfDefined('outlineOpacity', outlineOpacity, v => v / 100); setConfigValueIfDefined('outlineOpacity', outlineOpacity, v => v / 100);
setConfigValueIfDefined('fillAlpha', fillAlpha, v => v / 100); setConfigValueIfDefined('fillAlpha', fillAlpha, v => v / 100);
setConfigValueIfDefined('renderFill', renderFill); setConfigValueIfDefined('renderFill', renderFill);
setConfigValueIfDefined( setConfigValueIfDefined('fillAlphaInactive', fillAlphaInactive, v => v / 100);
'fillAlphaInactive',
fillAlphaInactive,
v => v / 100
);
setConfigValueIfDefined('outlineOpacityInactive', fillAlphaInactive, v => setConfigValueIfDefined('outlineOpacityInactive', fillAlphaInactive, v =>
Math.max(0.75, v / 100) Math.max(0.75, v / 100)
); );
@ -1435,10 +1301,7 @@ class SegmentationService extends PubSubService {
// }); // });
// } // }
this._broadcastEvent( this._broadcastEvent(this.EVENTS.SEGMENTATION_CONFIGURATION_CHANGED, this.getConfiguration());
this.EVENTS.SEGMENTATION_CONFIGURATION_CHANGED,
this.getConfiguration()
);
}; };
public getLabelmapVolume = (segmentationId: string) => { public getLabelmapVolume = (segmentationId: string) => {
@ -1472,9 +1335,7 @@ class SegmentationService extends PubSubService {
const segmentInfo = segmentation.segments[segmentIndex]; const segmentInfo = segmentation.segments[segmentIndex];
if (segmentInfo === undefined) { if (segmentInfo === undefined) {
throw new Error( throw new Error(`Segment ${segmentIndex} not yet added to segmentation: ${segmentationId}`);
`Segment ${segmentIndex} not yet added to segmentation: ${segmentationId}`
);
} }
segmentInfo.label = label; segmentInfo.label = label;
@ -1487,14 +1348,8 @@ class SegmentationService extends PubSubService {
} }
} }
public shouldRenderSegmentation( public shouldRenderSegmentation(viewportDisplaySetInstanceUIDs, segDisplaySetInstanceUID) {
viewportDisplaySetInstanceUIDs, if (!viewportDisplaySetInstanceUIDs || !viewportDisplaySetInstanceUIDs.length) {
segDisplaySetInstanceUID
) {
if (
!viewportDisplaySetInstanceUIDs ||
!viewportDisplaySetInstanceUIDs.length
) {
return false; return false;
} }
@ -1502,20 +1357,14 @@ class SegmentationService extends PubSubService {
let shouldDisplaySeg = false; let shouldDisplaySeg = false;
const segDisplaySet = displaySetService.getDisplaySetByUID( const segDisplaySet = displaySetService.getDisplaySetByUID(segDisplaySetInstanceUID);
segDisplaySetInstanceUID
);
const segFrameOfReferenceUID = this._getFrameOfReferenceUIDForSeg( const segFrameOfReferenceUID = this._getFrameOfReferenceUIDForSeg(segDisplaySet);
segDisplaySet
);
// check if the displaySet is sharing the same frameOfReferenceUID // check if the displaySet is sharing the same frameOfReferenceUID
// with the new segmentation // with the new segmentation
for (const displaySetInstanceUID of viewportDisplaySetInstanceUIDs) { for (const displaySetInstanceUID of viewportDisplaySetInstanceUIDs) {
const displaySet = displaySetService.getDisplaySetByUID( const displaySet = displaySetService.getDisplaySetByUID(displaySetInstanceUID);
displaySetInstanceUID
);
// Todo: this might not be ideal for use cases such as 4D, since we // Todo: this might not be ideal for use cases such as 4D, since we
// don't want to show the segmentation for all the frames // don't want to show the segmentation for all the frames
@ -1563,10 +1412,7 @@ class SegmentationService extends PubSubService {
segmentation.isActive = segmentation.id === segmentationId; segmentation.isActive = segmentation.id === segmentationId;
}); });
const representation = this._getSegmentationRepresentation( const representation = this._getSegmentationRepresentation(segmentationId, toolGroupId);
segmentationId,
toolGroupId
);
cstSegmentation.activeSegmentation.setActiveSegmentationRepresentation( cstSegmentation.activeSegmentation.setActiveSegmentationRepresentation(
toolGroupId, toolGroupId,
@ -1580,16 +1426,11 @@ class SegmentationService extends PubSubService {
} }
} }
private _toggleSegmentationVisibility = ( private _toggleSegmentationVisibility = (segmentationId: string, suppressEvents = false) => {
segmentationId: string,
suppressEvents = false
) => {
const segmentation = this.segmentations[segmentationId]; const segmentation = this.segmentations[segmentationId];
if (!segmentation) { if (!segmentation) {
throw new Error( throw new Error(`Segmentation with segmentationId ${segmentationId} not found.`);
`Segmentation with segmentationId ${segmentationId} not found.`
);
} }
segmentation.isVisible = !segmentation.isVisible; segmentation.isVisible = !segmentation.isVisible;
@ -1603,21 +1444,14 @@ class SegmentationService extends PubSubService {
} }
}; };
private _setActiveSegment( private _setActiveSegment(segmentationId: string, segmentIndex: number, suppressEvents = false) {
segmentationId: string,
segmentIndex: number,
suppressEvents = false
) {
const segmentation = this.getSegmentation(segmentationId); const segmentation = this.getSegmentation(segmentationId);
if (segmentation === undefined) { if (segmentation === undefined) {
throw new Error(`no segmentation for segmentationId: ${segmentationId}`); throw new Error(`no segmentation for segmentationId: ${segmentationId}`);
} }
cstSegmentation.segmentIndex.setActiveSegmentIndex( cstSegmentation.segmentIndex.setActiveSegmentIndex(segmentationId, segmentIndex);
segmentationId,
segmentIndex
);
segmentation.activeSegmentIndex = segmentIndex; segmentation.activeSegmentIndex = segmentIndex;
@ -1641,8 +1475,7 @@ class SegmentationService extends PubSubService {
} }
private _getVolumeIdForDisplaySet(displaySet) { private _getVolumeIdForDisplaySet(displaySet) {
const volumeLoaderSchema = const volumeLoaderSchema = displaySet.volumeLoaderSchema ?? VOLUME_LOADER_SCHEME;
displaySet.volumeLoaderSchema ?? VOLUME_LOADER_SCHEME;
return `${volumeLoaderSchema}:${displaySet.displaySetInstanceUID}`; return `${volumeLoaderSchema}:${displaySet.displaySetInstanceUID}`;
} }
@ -1663,9 +1496,7 @@ class SegmentationService extends PubSubService {
const segmentInfo = this._getSegmentInfo(segmentation, segmentIndex); const segmentInfo = this._getSegmentInfo(segmentation, segmentIndex);
if (segmentInfo === undefined) { if (segmentInfo === undefined) {
throw new Error( throw new Error(`Segment ${segmentIndex} not yet added to segmentation: ${segmentationId}`);
`Segment ${segmentIndex} not yet added to segmentation: ${segmentationId}`
);
} }
toolGroupId = toolGroupId ?? this._getFirstToolGroupId(); toolGroupId = toolGroupId ?? this._getFirstToolGroupId();
@ -1676,9 +1507,7 @@ class SegmentationService extends PubSubService {
); );
if (!segmentationRepresentation) { if (!segmentationRepresentation) {
throw new Error( throw new Error('Must add representation to toolgroup before setting segments');
'Must add representation to toolgroup before setting segments'
);
} }
const { segmentationRepresentationUID } = segmentationRepresentation; const { segmentationRepresentationUID } = segmentationRepresentation;
@ -1743,18 +1572,12 @@ class SegmentationService extends PubSubService {
const segmentInfo = this._getSegmentInfo(segmentation, segmentIndex); const segmentInfo = this._getSegmentInfo(segmentation, segmentIndex);
if (segmentInfo === undefined) { if (segmentInfo === undefined) {
throw new Error( throw new Error(`Segment ${segmentIndex} not yet added to segmentation: ${segmentationId}`);
`Segment ${segmentIndex} not yet added to segmentation: ${segmentationId}`
);
} }
segmentInfo.isLocked = isLocked; segmentInfo.isLocked = isLocked;
cstSegmentation.segmentLocking.setSegmentIndexLocked( cstSegmentation.segmentLocking.setSegmentIndexLocked(segmentationId, segmentIndex, isLocked);
segmentationId,
segmentIndex,
isLocked
);
if (suppressEvents === false) { if (suppressEvents === false) {
this._broadcastEvent(this.EVENTS.SEGMENTATION_UPDATED, { this._broadcastEvent(this.EVENTS.SEGMENTATION_UPDATED, {
@ -1772,10 +1595,10 @@ class SegmentationService extends PubSubService {
) { ) {
toolGroupId = toolGroupId ?? this._getFirstToolGroupId(); toolGroupId = toolGroupId ?? this._getFirstToolGroupId();
const { const { segmentationRepresentationUID, segmentation } = this._getSegmentationInfo(
segmentationRepresentationUID, segmentationId,
segmentation, toolGroupId
} = this._getSegmentationInfo(segmentationId, toolGroupId); );
if (segmentation === undefined) { if (segmentation === undefined) {
throw new Error(`no segmentation for segmentationId: ${segmentationId}`); throw new Error(`no segmentation for segmentationId: ${segmentationId}`);
@ -1784,9 +1607,7 @@ class SegmentationService extends PubSubService {
const segmentInfo = this._getSegmentInfo(segmentation, segmentIndex); const segmentInfo = this._getSegmentInfo(segmentation, segmentIndex);
if (segmentInfo === undefined) { if (segmentInfo === undefined) {
throw new Error( throw new Error(`Segment ${segmentIndex} not yet added to segmentation: ${segmentationId}`);
`Segment ${segmentIndex} not yet added to segmentation: ${segmentationId}`
);
} }
segmentInfo.isVisible = isVisible; segmentInfo.isVisible = isVisible;
@ -1828,9 +1649,7 @@ class SegmentationService extends PubSubService {
const segmentInfo = this._getSegmentInfo(segmentation, segmentIndex); const segmentInfo = this._getSegmentInfo(segmentation, segmentIndex);
if (segmentInfo === undefined) { if (segmentInfo === undefined) {
throw new Error( throw new Error(`Segment ${segmentIndex} not yet added to segmentation: ${segmentationId}`);
`Segment ${segmentIndex} not yet added to segmentation: ${segmentationId}`
);
} }
toolGroupId = toolGroupId ?? this._getFirstToolGroupId(); toolGroupId = toolGroupId ?? this._getFirstToolGroupId();
@ -1841,9 +1660,7 @@ class SegmentationService extends PubSubService {
); );
if (!segmentationRepresentation) { if (!segmentationRepresentation) {
throw new Error( throw new Error('Must add representation to toolgroup before setting segments');
'Must add representation to toolgroup before setting segments'
);
} }
const { segmentationRepresentationUID } = segmentationRepresentation; const { segmentationRepresentationUID } = segmentationRepresentation;
@ -1884,9 +1701,7 @@ class SegmentationService extends PubSubService {
const segmentInfo = this._getSegmentInfo(segmentation, segmentIndex); const segmentInfo = this._getSegmentInfo(segmentation, segmentIndex);
if (segmentInfo === undefined) { if (segmentInfo === undefined) {
throw new Error( throw new Error(`Segment ${segmentIndex} not yet added to segmentation: ${segmentationId}`);
`Segment ${segmentIndex} not yet added to segmentation: ${segmentationId}`
);
} }
segmentInfo.label = segmentLabel; segmentInfo.label = segmentLabel;
@ -1899,9 +1714,8 @@ class SegmentationService extends PubSubService {
} }
private _getSegmentationRepresentation(segmentationId, toolGroupId) { private _getSegmentationRepresentation(segmentationId, toolGroupId) {
const segmentationRepresentations = this.getSegmentationRepresentationsForToolGroup( const segmentationRepresentations =
toolGroupId this.getSegmentationRepresentationsForToolGroup(toolGroupId);
);
if (segmentationRepresentations.length === 0) { if (segmentationRepresentations.length === 0) {
return; return;
@ -1974,21 +1788,13 @@ class SegmentationService extends PubSubService {
return; return;
} }
const segmentationState = cstSegmentation.state.getSegmentation( const segmentationState = cstSegmentation.state.getSegmentation(segmentationId);
segmentationId
);
if (!segmentationState) { if (!segmentationState) {
return; return;
} }
const { const { activeSegmentIndex, cachedStats, segmentsLocked, label, type } = segmentationState;
activeSegmentIndex,
cachedStats,
segmentsLocked,
label,
type,
} = segmentationState;
if (![LABELMAP, CONTOUR].includes(type)) { if (![LABELMAP, CONTOUR].includes(type)) {
throw new Error( throw new Error(
@ -2018,10 +1824,7 @@ class SegmentationService extends PubSubService {
try { try {
this.addOrUpdateSegmentation(segmentationSchema); this.addOrUpdateSegmentation(segmentationSchema);
} catch (error) { } catch (error) {
console.warn( console.warn(`Failed to add/update segmentation ${segmentationId}`, error);
`Failed to add/update segmentation ${segmentationId}`,
error
);
} }
}; };
@ -2037,9 +1840,7 @@ class SegmentationService extends PubSubService {
); );
if (!segmentationRepresentation) { if (!segmentationRepresentation) {
throw new Error( throw new Error('Must add representation to toolgroup before setting segments');
'Must add representation to toolgroup before setting segments'
);
} }
const { segmentationRepresentationUID } = segmentationRepresentation; const { segmentationRepresentationUID } = segmentationRepresentation;
@ -2057,14 +1858,11 @@ class SegmentationService extends PubSubService {
return; return;
} }
const toolGroupIds = segmentationState.getToolGroupIdsWithSegmentation( const toolGroupIds = segmentationState.getToolGroupIdsWithSegmentation(segmentationId);
segmentationId
);
toolGroupIds.forEach(toolGroupId => { toolGroupIds.forEach(toolGroupId => {
const segmentationRepresentations = segmentationState.getSegmentationRepresentations( const segmentationRepresentations =
toolGroupId segmentationState.getSegmentationRepresentations(toolGroupId);
);
const UIDsToRemove = []; const UIDsToRemove = [];
segmentationRepresentations.forEach(representation => { segmentationRepresentations.forEach(representation => {
@ -2089,17 +1887,12 @@ class SegmentationService extends PubSubService {
} }
} }
private _updateCornerstoneSegmentations({ private _updateCornerstoneSegmentations({ segmentationId, notYetUpdatedAtSource }) {
segmentationId,
notYetUpdatedAtSource,
}) {
if (notYetUpdatedAtSource === false) { if (notYetUpdatedAtSource === false) {
return; return;
} }
const segmentationState = cstSegmentation.state; const segmentationState = cstSegmentation.state;
const sourceSegmentation = segmentationState.getSegmentation( const sourceSegmentation = segmentationState.getSegmentation(segmentationId);
segmentationId
);
const segmentation = this.segmentations[segmentationId]; const segmentation = this.segmentations[segmentationId];
const { label, cachedStats } = segmentation; const { label, cachedStats } = segmentation;
@ -2115,14 +1908,11 @@ class SegmentationService extends PubSubService {
private _updateCornerstoneSegmentationVisibility = segmentationId => { private _updateCornerstoneSegmentationVisibility = segmentationId => {
const segmentationState = cstSegmentation.state; const segmentationState = cstSegmentation.state;
const toolGroupIds = segmentationState.getToolGroupIdsWithSegmentation( const toolGroupIds = segmentationState.getToolGroupIdsWithSegmentation(segmentationId);
segmentationId
);
toolGroupIds.forEach(toolGroupId => { toolGroupIds.forEach(toolGroupId => {
const segmentationRepresentations = cstSegmentation.state.getSegmentationRepresentations( const segmentationRepresentations =
toolGroupId cstSegmentation.state.getSegmentationRepresentations(toolGroupId);
);
if (segmentationRepresentations.length === 0) { if (segmentationRepresentations.length === 0) {
return; return;
@ -2146,10 +1936,7 @@ class SegmentationService extends PubSubService {
); );
// update segments visibility // update segments visibility
const { segmentation } = this._getSegmentationInfo( const { segmentation } = this._getSegmentationInfo(segmentationId, toolGroupId);
segmentationId,
toolGroupId
);
const segments = segmentation.segments.filter(Boolean); const segments = segmentation.segments.filter(Boolean);
@ -2161,9 +1948,7 @@ class SegmentationService extends PubSubService {
private _getToolGroupIdsWithSegmentation(segmentationId: string) { private _getToolGroupIdsWithSegmentation(segmentationId: string) {
const segmentationState = cstSegmentation.state; const segmentationState = cstSegmentation.state;
const toolGroupIds = segmentationState.getToolGroupIdsWithSegmentation( const toolGroupIds = segmentationState.getToolGroupIdsWithSegmentation(segmentationId);
segmentationId
);
return toolGroupIds; return toolGroupIds;
} }

View File

@ -1,8 +1,4 @@
import { import { synchronizers, SynchronizerManager, Synchronizer } from '@cornerstonejs/tools';
synchronizers,
SynchronizerManager,
Synchronizer,
} from '@cornerstonejs/tools';
import { pubSubServiceInterface, Types, ServicesManager } from '@ohif/core'; import { pubSubServiceInterface, Types, ServicesManager } from '@ohif/core';
@ -14,10 +10,7 @@ const EVENTS = {
* @params options - are an optional set of options associated with the first * @params options - are an optional set of options associated with the first
* sync group declared. * sync group declared.
*/ */
export type SyncCreator = ( export type SyncCreator = (id: string, options?: Record<string, unknown>) => Synchronizer;
id: string,
options?: Record<string, unknown>
) => Synchronizer;
export type SyncGroup = { export type SyncGroup = {
type: string; type: string;
@ -40,9 +33,7 @@ export default class SyncGroupService {
static REGISTRATION = { static REGISTRATION = {
name: 'syncGroupService', name: 'syncGroupService',
altName: 'SyncGroupService', altName: 'SyncGroupService',
create: ({ create: ({ servicesManager }: Types.Extensions.ExtensionParams): SyncGroupService => {
servicesManager,
}: Types.Extensions.ExtensionParams): SyncGroupService => {
return new SyncGroupService(servicesManager); return new SyncGroupService(servicesManager);
}, },
}; };
@ -65,11 +56,7 @@ export default class SyncGroupService {
Object.assign(this, pubSubServiceInterface); Object.assign(this, pubSubServiceInterface);
} }
private _createSynchronizer( private _createSynchronizer(type: string, id: string, options): Synchronizer | undefined {
type: string,
id: string,
options
): Synchronizer | undefined {
const syncCreator = this.synchronizerCreators[type.toLowerCase()]; const syncCreator = this.synchronizerCreators[type.toLowerCase()];
if (syncCreator) { if (syncCreator) {
return syncCreator(id, options); return syncCreator(id, options);
@ -109,19 +96,11 @@ export default class SyncGroupService {
return; return;
} }
const syncGroupsArray = Array.isArray(syncGroups) const syncGroupsArray = Array.isArray(syncGroups) ? syncGroups : [syncGroups];
? syncGroups
: [syncGroups];
syncGroupsArray.forEach(syncGroup => { syncGroupsArray.forEach(syncGroup => {
const syncGroupObj = asSyncGroup(syncGroup); const syncGroupObj = asSyncGroup(syncGroup);
const { const { type, target = true, source = true, options = {}, id = type } = syncGroupObj;
type,
target = true,
source = true,
options = {},
id = type,
} = syncGroupObj;
const synchronizer = this._getOrCreateSynchronizer(type, id, options); const synchronizer = this._getOrCreateSynchronizer(type, id, options);
synchronizer.setOptions(viewportId, options); synchronizer.setOptions(viewportId, options);

View File

@ -24,9 +24,7 @@ export default class ToolGroupService {
public static REGISTRATION = { public static REGISTRATION = {
name: 'toolGroupService', name: 'toolGroupService',
altName: 'ToolGroupService', altName: 'ToolGroupService',
create: ({ create: ({ servicesManager }: OhifTypes.Extensions.ExtensionParams): ToolGroupService => {
servicesManager,
}: OhifTypes.Extensions.ExtensionParams): ToolGroupService => {
return new ToolGroupService(servicesManager); return new ToolGroupService(servicesManager);
}, },
}; };
@ -40,10 +38,7 @@ export default class ToolGroupService {
EVENTS: { [key: string]: string }; EVENTS: { [key: string]: string };
constructor(serviceManager) { constructor(serviceManager) {
const { const { cornerstoneViewportService, viewportGridService } = serviceManager.services;
cornerstoneViewportService,
viewportGridService,
} = serviceManager.services;
this.cornerstoneViewportService = cornerstoneViewportService; this.cornerstoneViewportService = cornerstoneViewportService;
this.viewportGridService = viewportGridService; this.viewportGridService = viewportGridService;
this.listeners = {}; this.listeners = {};
@ -66,19 +61,14 @@ export default class ToolGroupService {
if (!toolGroupIdToUse) { if (!toolGroupIdToUse) {
// Use the active viewport's tool group if no tool group id is provided // Use the active viewport's tool group if no tool group id is provided
const enabledElement = getActiveViewportEnabledElement( const enabledElement = getActiveViewportEnabledElement(this.viewportGridService);
this.viewportGridService
);
if (!enabledElement) { if (!enabledElement) {
return; return;
} }
const { renderingEngineId, viewportId } = enabledElement; const { renderingEngineId, viewportId } = enabledElement;
const toolGroup = ToolGroupManager.getToolGroupForViewport( const toolGroup = ToolGroupManager.getToolGroupForViewport(viewportId, renderingEngineId);
viewportId,
renderingEngineId
);
if (!toolGroup) { if (!toolGroup) {
console.warn( console.warn(
@ -103,10 +93,7 @@ export default class ToolGroupService {
public getToolGroupForViewport(viewportId: string): Types.IToolGroup | void { public getToolGroupForViewport(viewportId: string): Types.IToolGroup | void {
const renderingEngine = this.cornerstoneViewportService.getRenderingEngine(); const renderingEngine = this.cornerstoneViewportService.getRenderingEngine();
return ToolGroupManager.getToolGroupForViewport( return ToolGroupManager.getToolGroupForViewport(viewportId, renderingEngine.id);
viewportId,
renderingEngine.id
);
} }
public getActiveToolForViewport(viewportId: string): string { public getActiveToolForViewport(viewportId: string): string {
@ -133,10 +120,7 @@ export default class ToolGroupService {
renderingEngineId: string, renderingEngineId: string,
deleteToolGroupIfEmpty?: boolean deleteToolGroupIfEmpty?: boolean
): void { ): void {
const toolGroup = ToolGroupManager.getToolGroupForViewport( const toolGroup = ToolGroupManager.getToolGroupForViewport(viewportId, renderingEngineId);
viewportId,
renderingEngineId
);
if (!toolGroup) { if (!toolGroup) {
return; return;
@ -193,11 +177,7 @@ export default class ToolGroupService {
return toolGroup; return toolGroup;
} }
public addToolsToToolGroup( public addToolsToToolGroup(toolGroupId: string, tools: Array<Tool>, configs: any = {}): void {
toolGroupId: string,
tools: Array<Tool>,
configs: any = {}
): void {
const toolGroup = ToolGroupManager.getToolGroup(toolGroupId); const toolGroup = ToolGroupManager.getToolGroup(toolGroupId);
// this.changeConfigurationIfNecessary(toolGroup, volumeId); // this.changeConfigurationIfNecessary(toolGroup, volumeId);
this._addTools(toolGroup, tools, configs); this._addTools(toolGroup, tools, configs);

View File

@ -12,10 +12,7 @@ import {
Enums as csEnums, Enums as csEnums,
} from '@cornerstonejs/core'; } from '@cornerstonejs/core';
import { import { utilities as csToolsUtils, Enums as csToolsEnums } from '@cornerstonejs/tools';
utilities as csToolsUtils,
Enums as csToolsEnums,
} from '@cornerstonejs/tools';
import { IViewportService } from './IViewportService'; import { IViewportService } from './IViewportService';
import { RENDERING_ENGINE_ID } from './constants'; import { RENDERING_ENGINE_ID } from './constants';
import ViewportInfo, { import ViewportInfo, {
@ -23,25 +20,20 @@ import ViewportInfo, {
DisplaySetOptions, DisplaySetOptions,
PublicViewportOptions, PublicViewportOptions,
} from './Viewport'; } from './Viewport';
import { import { StackViewportData, VolumeViewportData } from '../../types/CornerstoneCacheService';
StackViewportData,
VolumeViewportData,
} from '../../types/CornerstoneCacheService';
import { Presentation, Presentations } from '../../types/Presentation'; import { Presentation, Presentations } from '../../types/Presentation';
import JumpPresets from '../../utils/JumpPresets'; import JumpPresets from '../../utils/JumpPresets';
const EVENTS = { const EVENTS = {
VIEWPORT_DATA_CHANGED: VIEWPORT_DATA_CHANGED: 'event::cornerstoneViewportService:viewportDataChanged',
'event::cornerstoneViewportService:viewportDataChanged',
}; };
/** /**
* Handles cornerstone viewport logic including enabling, disabling, and * Handles cornerstone viewport logic including enabling, disabling, and
* updating the viewport. * updating the viewport.
*/ */
class CornerstoneViewportService extends PubSubService class CornerstoneViewportService extends PubSubService implements IViewportService {
implements IViewportService {
static REGISTRATION = { static REGISTRATION = {
name: 'cornerstoneViewportService', name: 'cornerstoneViewportService',
altName: 'CornerstoneViewportService', altName: 'CornerstoneViewportService',
@ -182,8 +174,7 @@ class CornerstoneViewportService extends PubSubService
const camera = csViewport.getCamera(); const camera = csViewport.getCamera();
return { return {
presentationIds, presentationIds,
viewportType: viewportType: !viewportType || viewportType === 'stack' ? 'stack' : 'volume',
!viewportType || viewportType === 'stack' ? 'stack' : 'volume',
properties, properties,
initialImageIndex, initialImageIndex,
camera, camera,
@ -202,10 +193,7 @@ class CornerstoneViewportService extends PubSubService
if (!presentation || !presentation.presentationIds) { if (!presentation || !presentation.presentationIds) {
return; return;
} }
const { const { lutPresentationStore, positionPresentationStore } = stateSyncService.getState();
lutPresentationStore,
positionPresentationStore,
} = stateSyncService.getState();
const { presentationIds } = presentation; const { presentationIds } = presentation;
const { lutPresentationId, positionPresentationId } = presentationIds || {}; const { lutPresentationId, positionPresentationId } = presentationIds || {};
const storeState = {}; const storeState = {};
@ -258,12 +246,8 @@ class CornerstoneViewportService extends PubSubService
// override the viewportOptions and displaySetOptions with the public ones // override the viewportOptions and displaySetOptions with the public ones
// since those are the newly set ones, we set them here so that it handles defaults // since those are the newly set ones, we set them here so that it handles defaults
const displaySetOptions = viewportInfo.setPublicDisplaySetOptions( const displaySetOptions = viewportInfo.setPublicDisplaySetOptions(publicDisplaySetOptions);
publicDisplaySetOptions const viewportOptions = viewportInfo.setPublicViewportOptions(publicViewportOptions);
);
const viewportOptions = viewportInfo.setPublicViewportOptions(
publicViewportOptions
);
const element = viewportInfo.getElement(); const element = viewportInfo.getElement();
const type = viewportInfo.getViewportType(); const type = viewportInfo.getViewportType();
@ -317,11 +301,7 @@ class CornerstoneViewportService extends PubSubService
): Types.IStackViewport | Types.IVolumeViewport | null { ): Types.IStackViewport | Types.IVolumeViewport | null {
const viewportInfo = this.getViewportInfo(viewportId); const viewportInfo = this.getViewportInfo(viewportId);
if ( if (!viewportInfo || !this.renderingEngine || this.renderingEngine.hasBeenDestroyed) {
!viewportInfo ||
!this.renderingEngine ||
this.renderingEngine.hasBeenDestroyed
) {
return null; return null;
} }
@ -342,24 +322,15 @@ class CornerstoneViewportService extends PubSubService
): void { ): void {
const displaySetOptions = viewportInfo.getDisplaySetOptions(); const displaySetOptions = viewportInfo.getDisplaySetOptions();
const { const { imageIds, initialImageIndex, displaySetInstanceUID } = viewportData.data;
imageIds,
initialImageIndex,
displaySetInstanceUID,
} = viewportData.data;
this.viewportsDisplaySets.set(viewport.id, [displaySetInstanceUID]); this.viewportsDisplaySets.set(viewport.id, [displaySetInstanceUID]);
let initialImageIndexToUse = let initialImageIndexToUse =
presentations?.positionPresentation?.initialImageIndex ?? presentations?.positionPresentation?.initialImageIndex ?? initialImageIndex;
initialImageIndex;
if ( if (initialImageIndexToUse === undefined || initialImageIndexToUse === null) {
initialImageIndexToUse === undefined || initialImageIndexToUse = this._getInitialImageIndexForViewport(viewportInfo, imageIds) || 0;
initialImageIndexToUse === null
) {
initialImageIndexToUse =
this._getInitialImageIndexForViewport(viewportInfo, imageIds) || 0;
} }
const properties = { ...presentations.lutPresentation?.properties }; const properties = { ...presentations.lutPresentation?.properties };
@ -404,12 +375,8 @@ class CornerstoneViewportService extends PubSubService
if (viewportType === csEnums.ViewportType.STACK) { if (viewportType === csEnums.ViewportType.STACK) {
numberOfSlices = imageIds.length; numberOfSlices = imageIds.length;
} else if (viewportType === csEnums.ViewportType.ORTHOGRAPHIC) { } else if (viewportType === csEnums.ViewportType.ORTHOGRAPHIC) {
const viewport = this.getCornerstoneViewport( const viewport = this.getCornerstoneViewport(viewportInfo.getViewportId());
viewportInfo.getViewportId() const imageSliceData = csUtils.getImageSliceDataForVolumeViewport(viewport);
);
const imageSliceData = csUtils.getImageSliceDataForVolumeViewport(
viewport
);
if (!imageSliceData) { if (!imageSliceData) {
return; return;
@ -423,11 +390,7 @@ class CornerstoneViewportService extends PubSubService
return this._getInitialImageIndex(numberOfSlices, index, preset); return this._getInitialImageIndex(numberOfSlices, index, preset);
} }
_getInitialImageIndex( _getInitialImageIndex(numberOfSlices: number, imageIndex?: number, preset?: JumpPresets): number {
numberOfSlices: number,
imageIndex?: number,
preset?: JumpPresets
): number {
const lastSliceIndex = numberOfSlices - 1; const lastSliceIndex = numberOfSlices - 1;
if (imageIndex !== undefined) { if (imageIndex !== undefined) {
@ -449,9 +412,7 @@ class CornerstoneViewportService extends PubSubService
// it will jump to a different slice than the middle one which // it will jump to a different slice than the middle one which
// was the initial slice, and we have some tools such as Crosshairs // was the initial slice, and we have some tools such as Crosshairs
// which rely on a relative camera modifications and those will break. // which rely on a relative camera modifications and those will break.
return lastSliceIndex % 2 === 0 return lastSliceIndex % 2 === 0 ? lastSliceIndex / 2 : (lastSliceIndex + 1) / 2;
? lastSliceIndex / 2
: (lastSliceIndex + 1) / 2;
} }
return 0; return 0;
@ -521,22 +482,11 @@ class CornerstoneViewportService extends PubSubService
}); });
// This returns the async continuation only // This returns the async continuation only
return this.setVolumesForViewport( return this.setVolumesForViewport(viewport, volumeInputArray, presentations);
viewport,
volumeInputArray,
presentations
);
} }
public async setVolumesForViewport( public async setVolumesForViewport(viewport, volumeInputArray, presentations) {
viewport, const { displaySetService, toolGroupService } = this.servicesManager.services;
volumeInputArray,
presentations
) {
const {
displaySetService,
toolGroupService,
} = this.servicesManager.services;
const viewportInfo = this.getViewportInfo(viewport.id); const viewportInfo = this.getViewportInfo(viewport.id);
const displaySetOptions = viewportInfo.getDisplaySetOptions(); const displaySetOptions = viewportInfo.getDisplaySetOptions();
@ -594,10 +544,7 @@ class CornerstoneViewportService extends PubSubService
// associated with the primary displaySet // associated with the primary displaySet
// get segmentations only returns the hydrated segmentations // get segmentations only returns the hydrated segmentations
this._addSegmentationRepresentationToToolGroupIfNecessary( this._addSegmentationRepresentationToToolGroupIfNecessary(displaySetInstanceUIDs, viewport);
displaySetInstanceUIDs,
viewport
);
} }
const toolGroup = toolGroupService.getToolGroupForViewport(viewport.id); const toolGroup = toolGroupService.getToolGroupForViewport(viewport.id);
@ -618,10 +565,7 @@ class CornerstoneViewportService extends PubSubService
displaySetInstanceUIDs: string[], displaySetInstanceUIDs: string[],
viewport: any viewport: any
) { ) {
const { const { segmentationService, toolGroupService } = this.servicesManager.services;
segmentationService,
toolGroupService,
} = this.servicesManager.services;
const toolGroup = toolGroupService.getToolGroupForViewport(viewport.id); const toolGroup = toolGroupService.getToolGroupForViewport(viewport.id);
@ -630,9 +574,7 @@ class CornerstoneViewportService extends PubSubService
for (const segmentation of segmentations) { for (const segmentation of segmentations) {
const toolGroupSegmentationRepresentations = const toolGroupSegmentationRepresentations =
segmentationService.getSegmentationRepresentationsForToolGroup( segmentationService.getSegmentationRepresentationsForToolGroup(toolGroup.id) || [];
toolGroup.id
) || [];
// if there is already a segmentation representation for this segmentation // if there is already a segmentation representation for this segmentation
// for this toolGroup, don't bother at all // for this toolGroup, don't bother at all
@ -647,16 +589,12 @@ class CornerstoneViewportService extends PubSubService
// otherwise, check if the hydrated segmentations are in the same FOR // otherwise, check if the hydrated segmentations are in the same FOR
// as the primary displaySet, if so add the representation (since it was not there) // as the primary displaySet, if so add the representation (since it was not there)
const { id: segDisplaySetInstanceUID, type } = segmentation; const { id: segDisplaySetInstanceUID, type } = segmentation;
const segFrameOfReferenceUID = this._getFrameOfReferenceUID( const segFrameOfReferenceUID = this._getFrameOfReferenceUID(segDisplaySetInstanceUID);
segDisplaySetInstanceUID
);
let shouldDisplaySeg = false; let shouldDisplaySeg = false;
for (const displaySetInstanceUID of displaySetInstanceUIDs) { for (const displaySetInstanceUID of displaySetInstanceUIDs) {
const primaryFrameOfReferenceUID = this._getFrameOfReferenceUID( const primaryFrameOfReferenceUID = this._getFrameOfReferenceUID(displaySetInstanceUID);
displaySetInstanceUID
);
if (segFrameOfReferenceUID === primaryFrameOfReferenceUID) { if (segFrameOfReferenceUID === primaryFrameOfReferenceUID) {
shouldDisplaySeg = true; shouldDisplaySeg = true;
@ -677,14 +615,8 @@ class CornerstoneViewportService extends PubSubService
} }
} }
private addOverlayRepresentationForDisplaySet( private addOverlayRepresentationForDisplaySet(displaySet: any, viewport: any) {
displaySet: any, const { segmentationService, toolGroupService } = this.servicesManager.services;
viewport: any
) {
const {
segmentationService,
toolGroupService,
} = this.servicesManager.services;
const { referencedVolumeId } = displaySet; const { referencedVolumeId } = displaySet;
const segmentationId = displaySet.displaySetInstanceUID; const segmentationId = displaySet.displaySetInstanceUID;
@ -711,10 +643,7 @@ class CornerstoneViewportService extends PubSubService
const viewport = this.getCornerstoneViewport(viewportId); const viewport = this.getCornerstoneViewport(viewportId);
const viewportCamera = viewport.getCamera(); const viewportCamera = viewport.getCamera();
if ( if (viewport instanceof VolumeViewport || viewport instanceof VolumeViewport3D) {
viewport instanceof VolumeViewport ||
viewport instanceof VolumeViewport3D
) {
this._setVolumeViewport(viewport, viewportData, viewportInfo).then(() => { this._setVolumeViewport(viewport, viewportData, viewportInfo).then(() => {
if (keepCamera) { if (keepCamera) {
viewport.setCamera(viewportCamera); viewport.setCamera(viewportCamera);
@ -744,10 +673,7 @@ class CornerstoneViewportService extends PubSubService
viewportInfo, viewportInfo,
presentations presentations
); );
} else if ( } else if (viewport instanceof VolumeViewport || viewport instanceof VolumeViewport3D) {
viewport instanceof VolumeViewport ||
viewport instanceof VolumeViewport3D
) {
this._setVolumeViewport( this._setVolumeViewport(
viewport, viewport,
viewportData as VolumeViewportData, viewportData as VolumeViewportData,
@ -770,10 +696,7 @@ class CornerstoneViewportService extends PubSubService
_getSlabThickness(displaySetOptions, volumeId) { _getSlabThickness(displaySetOptions, volumeId) {
const { blendMode } = displaySetOptions; const { blendMode } = displaySetOptions;
if ( if (blendMode === undefined || displaySetOptions.slabThickness === undefined) {
blendMode === undefined ||
displaySetOptions.slabThickness === undefined
) {
return; return;
} }
@ -799,9 +722,7 @@ class CornerstoneViewportService extends PubSubService
_getFrameOfReferenceUID(displaySetInstanceUID) { _getFrameOfReferenceUID(displaySetInstanceUID) {
const { displaySetService } = this.servicesManager.services; const { displaySetService } = this.servicesManager.services;
const displaySet = displaySetService.getDisplaySetByUID( const displaySet = displaySetService.getDisplaySetByUID(displaySetInstanceUID);
displaySetInstanceUID
);
if (!displaySet) { if (!displaySet) {
return; return;

View File

@ -1,10 +1,6 @@
import { Types } from '@cornerstonejs/core'; import { Types } from '@cornerstonejs/core';
import { StackData, VolumeData } from '../../types/CornerstoneCacheService'; import { StackData, VolumeData } from '../../types/CornerstoneCacheService';
import { import { DisplaySetOptions, PublicViewportOptions, ViewportOptions } from './Viewport';
DisplaySetOptions,
PublicViewportOptions,
ViewportOptions,
} from './Viewport';
/** /**
* Handles cornerstone viewport logic including enabling, disabling, and * Handles cornerstone viewport logic including enabling, disabling, and

View File

@ -1,9 +1,6 @@
import { Types, Enums } from '@cornerstonejs/core'; import { Types, Enums } from '@cornerstonejs/core';
import { Types as UITypes } from '@ohif/ui'; import { Types as UITypes } from '@ohif/ui';
import { import { StackViewportData, VolumeViewportData } from '../../types/CornerstoneCacheService';
StackViewportData,
VolumeViewportData,
} from '../../types/CornerstoneCacheService';
import getCornerstoneBlendMode from '../../utils/getCornerstoneBlendMode'; import getCornerstoneBlendMode from '../../utils/getCornerstoneBlendMode';
import getCornerstoneOrientation from '../../utils/getCornerstoneOrientation'; import getCornerstoneOrientation from '../../utils/getCornerstoneOrientation';
import getCornerstoneViewportType from '../../utils/getCornerstoneViewportType'; import getCornerstoneViewportType from '../../utils/getCornerstoneViewportType';
@ -90,11 +87,7 @@ const DEFAULT_TOOLGROUP_ID = 'default';
// Return true if the data contains the given display set UID OR the imageId // Return true if the data contains the given display set UID OR the imageId
// if it is a composite object. // if it is a composite object.
const dataContains = ( const dataContains = (data, displaySetUID: string, imageId?: string): boolean => {
data,
displaySetUID: string,
imageId?: string
): boolean => {
if (data.displaySetInstanceUID === displaySetUID) { if (data.displaySetInstanceUID === displaySetUID) {
return true; return true;
} }
@ -128,9 +121,7 @@ class ViewportInfo {
} }
if (this.viewportData.data.length) { if (this.viewportData.data.length) {
return !!this.viewportData.data.find(data => return !!this.viewportData.data.find(data => dataContains(data, displaySetUID, imageId));
dataContains(data, displaySetUID, imageId)
);
} }
return dataContains(this.viewportData.data, displaySetUID, imageId); return dataContains(this.viewportData.data, displaySetUID, imageId);
} }
@ -158,9 +149,7 @@ class ViewportInfo {
this.element = element; this.element = element;
} }
public setViewportData( public setViewportData(viewportData: StackViewportData | VolumeViewportData): void {
viewportData: StackViewportData | VolumeViewportData
): void {
this.viewportData = viewportData; this.viewportData = viewportData;
} }
@ -168,7 +157,6 @@ class ViewportInfo {
return this.viewportData; return this.viewportData;
} }
public getElement(): HTMLDivElement { public getElement(): HTMLDivElement {
return this.element; return this.element;
} }
@ -181,13 +169,11 @@ class ViewportInfo {
publicDisplaySetOptions: PublicDisplaySetOptions[] | DisplaySetSelector[] publicDisplaySetOptions: PublicDisplaySetOptions[] | DisplaySetSelector[]
): Array<DisplaySetOptions> { ): Array<DisplaySetOptions> {
// map the displaySetOptions and check if they are undefined then set them to default values // map the displaySetOptions and check if they are undefined then set them to default values
const displaySetOptions = this.mapDisplaySetOptions( const displaySetOptions = this.mapDisplaySetOptions(publicDisplaySetOptions);
publicDisplaySetOptions
);
this.setDisplaySetOptions(displaySetOptions); this.setDisplaySetOptions(displaySetOptions);
return this.displaySetOptions return this.displaySetOptions;
} }
public hasDisplaySet(displaySetInstanceUID: string): boolean { public hasDisplaySet(displaySetInstanceUID: string): boolean {
@ -196,7 +182,8 @@ class ViewportInfo {
// via cornerstoneViewportService // via cornerstoneViewportService
let viewportData = this.getViewportData(); let viewportData = this.getViewportData();
if (viewportData.viewportType === Enums.ViewportType.ORTHOGRAPHIC || if (
viewportData.viewportType === Enums.ViewportType.ORTHOGRAPHIC ||
viewportData.viewportType === Enums.ViewportType.VOLUME_3D viewportData.viewportType === Enums.ViewportType.VOLUME_3D
) { ) {
viewportData = viewportData as VolumeViewportData; viewportData = viewportData as VolumeViewportData;
@ -209,22 +196,15 @@ class ViewportInfo {
return viewportData.data.displaySetInstanceUID === displaySetInstanceUID; return viewportData.data.displaySetInstanceUID === displaySetInstanceUID;
} }
public setPublicViewportOptions( public setPublicViewportOptions(viewportOptionsEntry: PublicViewportOptions): ViewportOptions {
viewportOptionsEntry: PublicViewportOptions
): ViewportOptions {
let viewportType = viewportOptionsEntry.viewportType; let viewportType = viewportOptionsEntry.viewportType;
const { const { toolGroupId = DEFAULT_TOOLGROUP_ID, presentationIds } = viewportOptionsEntry;
toolGroupId = DEFAULT_TOOLGROUP_ID,
presentationIds,
} = viewportOptionsEntry;
let orientation; let orientation;
if (!viewportType) { if (!viewportType) {
viewportType = getCornerstoneViewportType(STACK); viewportType = getCornerstoneViewportType(STACK);
} else { } else {
viewportType = getCornerstoneViewportType( viewportType = getCornerstoneViewportType(viewportOptionsEntry.viewportType);
viewportOptionsEntry.viewportType
);
} }
// map SAGITTAL, AXIAL, CORONAL orientation to be used by cornerstone // map SAGITTAL, AXIAL, CORONAL orientation to be used by cornerstone
@ -256,9 +236,7 @@ class ViewportInfo {
return this.viewportOptions; return this.viewportOptions;
} }
public setDisplaySetOptions( public setDisplaySetOptions(displaySetOptions: Array<DisplaySetOptions>): void {
displaySetOptions: Array<DisplaySetOptions>
): void {
this.displaySetOptions = displaySetOptions; this.displaySetOptions = displaySetOptions;
} }

View File

@ -9,11 +9,7 @@ const state = {
* @param {HTMLElement} dom Active viewport element. * @param {HTMLElement} dom Active viewport element.
* @return void * @return void
*/ */
const setEnabledElement = ( const setEnabledElement = (viewportId: string, element: HTMLElement, context?: string): void => {
viewportId: string,
element: HTMLElement,
context?: string
): void => {
const targetContext = context || state.DEFAULT_CONTEXT; const targetContext = context || state.DEFAULT_CONTEXT;
state.enabledElements[viewportId] = { state.enabledElements[viewportId] = {

View File

@ -25,8 +25,7 @@ class CalibrationLineTool extends LengthTool {
this._renderingViewport.worldToCanvas(p) this._renderingViewport.worldToCanvas(p)
); );
// for display, round to 2 decimal points // for display, round to 2 decimal points
const lengthPx = const lengthPx = Math.round(calculateLength2(canvasPoint1, canvasPoint2) * 100) / 100;
Math.round(calculateLength2(canvasPoint1, canvasPoint2) * 100) / 100;
const textLines = [`${lengthPx}px`]; const textLines = [`${lengthPx}px`];
@ -64,17 +63,11 @@ export function onCompletedCalibrationLine(servicesManager, csToolsEvent) {
const length = const length =
Math.round( Math.round(
calculateLength3( calculateLength3(annotationData.handles.points[0], annotationData.handles.points[1]) * 100
annotationData.handles.points[0],
annotationData.handles.points[1]
) * 100
) / 100; ) / 100;
// calculate the currently applied pixel spacing on the viewport // calculate the currently applied pixel spacing on the viewport
const calibratedPixelSpacing = metaData.get( const calibratedPixelSpacing = metaData.get('calibratedPixelSpacing', imageId);
'calibratedPixelSpacing',
imageId
);
const imagePlaneModule = metaData.get('imagePlaneModule', imageId); const imagePlaneModule = metaData.get('imagePlaneModule', imageId);
const currentRowPixelSpacing = const currentRowPixelSpacing =
calibratedPixelSpacing?.[0] || imagePlaneModule?.rowPixelSpacing || 1; calibratedPixelSpacing?.[0] || imagePlaneModule?.rowPixelSpacing || 1;
@ -87,12 +80,7 @@ export function onCompletedCalibrationLine(servicesManager, csToolsEvent) {
const colSpacing = spacingScale * currentColumnPixelSpacing; const colSpacing = spacingScale * currentColumnPixelSpacing;
// trigger resize of the viewport to adjust the world/pixel mapping // trigger resize of the viewport to adjust the world/pixel mapping
calibrateImageSpacing( calibrateImageSpacing(imageId, viewport.getRenderingEngine(), rowSpacing, colSpacing);
imageId,
viewport.getRenderingEngine(),
rowSpacing,
colSpacing
);
}; };
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {

View File

@ -1,7 +1,4 @@
import * as CornerstoneCacheService from './CornerstoneCacheService' import * as CornerstoneCacheService from './CornerstoneCacheService';
import CornerstoneServices from './CornerstoneServices'; import CornerstoneServices from './CornerstoneServices';
export type { export type { CornerstoneCacheService, CornerstoneServices };
CornerstoneCacheService,
CornerstoneServices,
}

View File

@ -27,18 +27,11 @@ const CornerstoneViewportDownloadForm = ({
const activeViewportElement = enabledElement?.element; const activeViewportElement = enabledElement?.element;
const activeViewportEnabledElement = getEnabledElement(activeViewportElement); const activeViewportEnabledElement = getEnabledElement(activeViewportElement);
const { const { viewportId: activeViewportId, renderingEngineId } = activeViewportEnabledElement;
viewportId: activeViewportId,
renderingEngineId,
} = activeViewportEnabledElement;
const toolGroup = ToolGroupManager.getToolGroupForViewport( const toolGroup = ToolGroupManager.getToolGroupForViewport(activeViewportId, renderingEngineId);
activeViewportId,
renderingEngineId
);
const toolModeAndBindings = Object.keys(toolGroup.toolOptions).reduce( const toolModeAndBindings = Object.keys(toolGroup.toolOptions).reduce((acc, toolName) => {
(acc, toolName) => {
const tool = toolGroup.toolOptions[toolName]; const tool = toolGroup.toolOptions[toolName];
const { mode, bindings } = tool; const { mode, bindings } = tool;
@ -49,9 +42,7 @@ const CornerstoneViewportDownloadForm = ({
bindings, bindings,
}, },
}; };
}, }, {});
{}
);
useEffect(() => { useEffect(() => {
return () => { return () => {
@ -64,9 +55,7 @@ const CornerstoneViewportDownloadForm = ({
const enableViewport = viewportElement => { const enableViewport = viewportElement => {
if (viewportElement) { if (viewportElement) {
const { renderingEngine, viewport } = getEnabledElement( const { renderingEngine, viewport } = getEnabledElement(activeViewportElement);
activeViewportElement
);
const viewportInput = { const viewportInput = {
viewportId: VIEWPORT_ID, viewportId: VIEWPORT_ID,
@ -91,11 +80,7 @@ const CornerstoneViewportDownloadForm = ({
} }
}; };
const updateViewportPreview = ( const updateViewportPreview = (downloadViewportElement, internalCanvas, fileType) =>
downloadViewportElement,
internalCanvas,
fileType
) =>
new Promise(resolve => { new Promise(resolve => {
const enabledElement = getEnabledElement(downloadViewportElement); const enabledElement = getEnabledElement(downloadViewportElement);
@ -133,10 +118,7 @@ const CornerstoneViewportDownloadForm = ({
resolve({ dataUrl, width: newWidth, height: newHeight }); resolve({ dataUrl, width: newWidth, height: newHeight });
downloadViewportElement.removeEventListener( downloadViewportElement.removeEventListener(Enums.Events.IMAGE_RENDERED, updateViewport);
Enums.Events.IMAGE_RENDERED,
updateViewport
);
} }
); );
}); });
@ -144,9 +126,7 @@ const CornerstoneViewportDownloadForm = ({
const loadImage = (activeViewportElement, viewportElement, width, height) => const loadImage = (activeViewportElement, viewportElement, width, height) =>
new Promise(resolve => { new Promise(resolve => {
if (activeViewportElement && viewportElement) { if (activeViewportElement && viewportElement) {
const activeViewportEnabledElement = getEnabledElement( const activeViewportEnabledElement = getEnabledElement(activeViewportElement);
activeViewportElement
);
if (!activeViewportEnabledElement) { if (!activeViewportEnabledElement) {
return; return;
@ -165,10 +145,7 @@ const CornerstoneViewportDownloadForm = ({
downloadViewport.setProperties(properties); downloadViewport.setProperties(properties);
const newWidth = Math.min(width || image.width, MAX_TEXTURE_SIZE); const newWidth = Math.min(width || image.width, MAX_TEXTURE_SIZE);
const newHeight = Math.min( const newHeight = Math.min(height || image.height, MAX_TEXTURE_SIZE);
height || image.height,
MAX_TEXTURE_SIZE
);
resolve({ width: newWidth, height: newHeight }); resolve({ width: newWidth, height: newHeight });
}); });
@ -190,31 +167,19 @@ const CornerstoneViewportDownloadForm = ({
} }
}); });
const toggleAnnotations = ( const toggleAnnotations = (toggle, viewportElement, activeViewportElement) => {
toggle, const activeViewportEnabledElement = getEnabledElement(activeViewportElement);
viewportElement,
activeViewportElement
) => {
const activeViewportEnabledElement = getEnabledElement(
activeViewportElement
);
const downloadViewportElement = getEnabledElement(viewportElement); const downloadViewportElement = getEnabledElement(viewportElement);
const { const { viewportId: activeViewportId, renderingEngineId } = activeViewportEnabledElement;
viewportId: activeViewportId,
renderingEngineId,
} = activeViewportEnabledElement;
const { viewportId: downloadViewportId } = downloadViewportElement; const { viewportId: downloadViewportId } = downloadViewportElement;
if (!activeViewportEnabledElement || !downloadViewportElement) { if (!activeViewportEnabledElement || !downloadViewportElement) {
return; return;
} }
const toolGroup = ToolGroupManager.getToolGroupForViewport( const toolGroup = ToolGroupManager.getToolGroupForViewport(activeViewportId, renderingEngineId);
activeViewportId,
renderingEngineId
);
// add the viewport to the toolGroup // add the viewport to the toolGroup
toolGroup.addViewport(downloadViewportId, renderingEngineId); toolGroup.addViewport(downloadViewportId, renderingEngineId);

View File

@ -98,22 +98,13 @@ export default class DicomFileUploader extends PubSubService {
}); });
}, },
timeout: () => { timeout: () => {
this._reject( this._reject(reject, new UploadRejection(UploadStatus.Failed, 'The request timed out.'));
reject,
new UploadRejection(UploadStatus.Failed, 'The request timed out.')
);
}, },
abort: () => { abort: () => {
this._reject( this._reject(reject, new UploadRejection(UploadStatus.Cancelled, 'Cancelled'));
reject,
new UploadRejection(UploadStatus.Cancelled, 'Cancelled')
);
}, },
error: () => { error: () => {
this._reject( this._reject(reject, new UploadRejection(UploadStatus.Failed, 'The request failed.'));
reject,
new UploadRejection(UploadStatus.Failed, 'The request failed.')
);
}, },
}; };
@ -122,10 +113,7 @@ export default class DicomFileUploader extends PubSubService {
.loadFileRequest(this._fileId) .loadFileRequest(this._fileId)
.then(dicomFile => { .then(dicomFile => {
if (this._abortController.signal.aborted) { if (this._abortController.signal.aborted) {
this._reject( this._reject(reject, new UploadRejection(UploadStatus.Cancelled, 'Cancelled'));
reject,
new UploadRejection(UploadStatus.Cancelled, 'Cancelled')
);
return; return;
} }
@ -133,10 +121,7 @@ export default class DicomFileUploader extends PubSubService {
// The file is not DICOM // The file is not DICOM
this._reject( this._reject(
reject, reject,
new UploadRejection( new UploadRejection(UploadStatus.Failed, 'Not a valid DICOM file.')
UploadStatus.Failed,
'Not a valid DICOM file.'
)
); );
return; return;
} }
@ -164,10 +149,7 @@ export default class DicomFileUploader extends PubSubService {
} }
private _isRejected(): boolean { private _isRejected(): boolean {
return ( return this._status === UploadStatus.Failed || this._status === UploadStatus.Cancelled;
this._status === UploadStatus.Failed ||
this._status === UploadStatus.Cancelled
);
} }
private _reject(reject: (reason?: any) => void, reason: any) { private _reject(reject: (reason?: any) => void, reason: any) {

View File

@ -21,11 +21,7 @@ function callInputDialog(
dialogConfig: any = {} dialogConfig: any = {}
) { ) {
const dialogId = 'dialog-enter-annotation'; const dialogId = 'dialog-enter-annotation';
const label = data const label = data ? (isArrowAnnotateInputDialog ? data.text : data.label) : '';
? isArrowAnnotateInputDialog
? data.text
: data.label
: '';
const { const {
dialogTitle = 'Annotation', dialogTitle = 'Annotation',
inputLabel = 'Enter your annotation', inputLabel = 'Enter your annotation',
@ -69,7 +65,7 @@ function callInputDialog(
return ( return (
<Input <Input
autoFocus autoFocus
className="bg-black border-primary-main" className="border-primary-main bg-black"
type="text" type="text"
id="annotation" id="annotation"
label={inputLabel} label={inputLabel}

View File

@ -8,9 +8,7 @@ const getImageId = imageObj => {
return; return;
} }
return typeof imageObj.getImageId === 'function' return typeof imageObj.getImageId === 'function' ? imageObj.getImageId() : imageObj.url;
? imageObj.getImageId()
: imageObj.url;
}; };
const findImageIdOnStudies = (studies, displaySetInstanceUID) => { const findImageIdOnStudies = (studies, displaySetInstanceUID) => {
@ -163,9 +161,7 @@ class DicomLoaderService {
getDicomDataMethod = fetchIt.bind(this, imageId); getDicomDataMethod = fetchIt.bind(this, imageId);
break; break;
default: default:
throw new Error( throw new Error(`Unsupported image type: ${loaderType} for imageId: ${imageId}`);
`Unsupported image type: ${loaderType} for imageId: ${imageId}`
);
} }
return getDicomDataMethod(); return getDicomDataMethod();

View File

@ -3,9 +3,7 @@ import { IEnabledElement } from '@cornerstonejs/core/dist/esm/types';
import { getEnabledElement as OHIFgetEnabledElement } from '../state'; import { getEnabledElement as OHIFgetEnabledElement } from '../state';
export default function getActiveViewportEnabledElement( export default function getActiveViewportEnabledElement(viewportGridService): IEnabledElement {
viewportGridService
): IEnabledElement {
const { activeViewportId } = viewportGridService.getState(); const { activeViewportId } = viewportGridService.getState();
const { element } = OHIFgetEnabledElement(activeViewportId) || {}; const { element } = OHIFgetEnabledElement(activeViewportId) || {};
const enabledElement = getEnabledElement(element); const enabledElement = getEnabledElement(element);

View File

@ -2,9 +2,7 @@ import { Enums } from '@cornerstonejs/core';
const MIP = 'mip'; const MIP = 'mip';
export default function getCornerstoneBlendMode( export default function getCornerstoneBlendMode(blendMode: string): Enums.BlendModes {
blendMode: string
): Enums.BlendModes {
if (!blendMode) { if (!blendMode) {
return Enums.BlendModes.COMPOSITE; return Enums.BlendModes.COMPOSITE;
} }

View File

@ -4,9 +4,7 @@ const AXIAL = 'axial';
const SAGITTAL = 'sagittal'; const SAGITTAL = 'sagittal';
const CORONAL = 'coronal'; const CORONAL = 'coronal';
export default function getCornerstoneOrientation( export default function getCornerstoneOrientation(orientation: string): Enums.OrientationAxis {
orientation: string
): Enums.OrientationAxis {
if (orientation) { if (orientation) {
switch (orientation.toLowerCase()) { switch (orientation.toLowerCase()) {
case AXIAL: case AXIAL:

View File

@ -5,9 +5,7 @@ const VOLUME = 'volume';
const ORTHOGRAPHIC = 'orthographic'; const ORTHOGRAPHIC = 'orthographic';
const VOLUME_3D = 'volume3d'; const VOLUME_3D = 'volume3d';
export default function getCornerstoneViewportType( export default function getCornerstoneViewportType(viewportType: string): Enums.ViewportType {
viewportType: string
): Enums.ViewportType {
const lowerViewportType = viewportType.toLowerCase(); const lowerViewportType = viewportType.toLowerCase();
if (lowerViewportType === STACK) { if (lowerViewportType === STACK) {
return Enums.ViewportType.STACK; return Enums.ViewportType.STACK;
@ -21,7 +19,5 @@ export default function getCornerstoneViewportType(
return Enums.ViewportType.VOLUME_3D; return Enums.ViewportType.VOLUME_3D;
} }
throw new Error( throw new Error(`Invalid viewport type: ${viewportType}. Valid types are: stack, volume`);
`Invalid viewport type: ${viewportType}. Valid types are: stack, volume`
);
} }

View File

@ -19,11 +19,7 @@ export default function getNthFrames(imageIds) {
const centerEnd = centerStart + 6; const centerEnd = centerStart + 6;
for (let i = 0; i < imageIds.length; i++) { for (let i = 0; i < imageIds.length; i++) {
if ( if (i < 2 || i > imageIds.length - 4 || (i > centerStart && i < centerEnd)) {
i < 2 ||
i > imageIds.length - 4 ||
(i > centerStart && i < centerEnd)
) {
frames[0].push(imageIds[i]); frames[0].push(imageIds[i]);
} else if (i % 7 === 2) { } else if (i % 7 === 2) {
frames[1].push(imageIds[i]); frames[1].push(imageIds[i]);
@ -33,12 +29,6 @@ export default function getNthFrames(imageIds) {
frames[(i % 2) + 3].push(imageIds[i]); frames[(i % 2) + 3].push(imageIds[i]);
} }
} }
const ret = [ const ret = [...frames[0], ...frames[1], ...frames[2], ...frames[3], ...frames[4]];
...frames[0],
...frames[1],
...frames[2],
...frames[3],
...frames[4],
];
return ret; return ret;
} }

View File

@ -105,9 +105,7 @@ export default function interleaveCenterLoader({
const { imageId } = request; const { imageId } = request;
AllRequests.forEach(volumeRequests => { AllRequests.forEach(volumeRequests => {
const volumeImageIdRequest = volumeRequests.find( const volumeImageIdRequest = volumeRequests.find(req => req.imageId === imageId);
req => req.imageId === imageId
);
if (volumeImageIdRequest) { if (volumeImageIdRequest) {
finalRequests.push(volumeImageIdRequest); finalRequests.push(volumeImageIdRequest);
} }
@ -117,31 +115,17 @@ export default function interleaveCenterLoader({
const requestType = Enums.RequestType.Prefetch; const requestType = Enums.RequestType.Prefetch;
const priority = 0; const priority = 0;
finalRequests.forEach( finalRequests.forEach(({ callLoadImage, additionalDetails, imageId, imageIdIndex, options }) => {
({ callLoadImage, additionalDetails, imageId, imageIdIndex, options }) => { const callLoadImageBound = callLoadImage.bind(null, imageId, imageIdIndex, options);
const callLoadImageBound = callLoadImage.bind(
null,
imageId,
imageIdIndex,
options
);
imageLoadPoolManager.addRequest( imageLoadPoolManager.addRequest(callLoadImageBound, requestType, additionalDetails, priority);
callLoadImageBound, });
requestType,
additionalDetails,
priority
);
}
);
// clear the volumeIdMapsToLoad // clear the volumeIdMapsToLoad
volumeIdMapsToLoad.clear(); volumeIdMapsToLoad.clear();
// copy the viewportIdVolumeInputArrayMap // copy the viewportIdVolumeInputArrayMap
const viewportIdVolumeInputArrayMapCopy = new Map( const viewportIdVolumeInputArrayMapCopy = new Map(viewportIdVolumeInputArrayMap);
viewportIdVolumeInputArrayMap
);
// reset the viewportIdVolumeInputArrayMap // reset the viewportIdVolumeInputArrayMap
viewportIdVolumeInputArrayMap.clear(); viewportIdVolumeInputArrayMap.clear();

View File

@ -94,9 +94,7 @@ export default function interleaveTopToBottom({
const { imageId } = request; const { imageId } = request;
AllRequests.forEach(volumeRequests => { AllRequests.forEach(volumeRequests => {
const volumeImageIdRequest = volumeRequests.find( const volumeImageIdRequest = volumeRequests.find(req => req.imageId === imageId);
req => req.imageId === imageId
);
if (volumeImageIdRequest) { if (volumeImageIdRequest) {
finalRequests.push(volumeImageIdRequest); finalRequests.push(volumeImageIdRequest);
} }
@ -106,31 +104,17 @@ export default function interleaveTopToBottom({
const requestType = Enums.RequestType.Prefetch; const requestType = Enums.RequestType.Prefetch;
const priority = 0; const priority = 0;
finalRequests.forEach( finalRequests.forEach(({ callLoadImage, additionalDetails, imageId, imageIdIndex, options }) => {
({ callLoadImage, additionalDetails, imageId, imageIdIndex, options }) => { const callLoadImageBound = callLoadImage.bind(null, imageId, imageIdIndex, options);
const callLoadImageBound = callLoadImage.bind(
null,
imageId,
imageIdIndex,
options
);
imageLoadPoolManager.addRequest( imageLoadPoolManager.addRequest(callLoadImageBound, requestType, additionalDetails, priority);
callLoadImageBound, });
requestType,
additionalDetails,
priority
);
}
);
// clear the volumeIdMapsToLoad // clear the volumeIdMapsToLoad
volumeIdMapsToLoad.clear(); volumeIdMapsToLoad.clear();
// copy the viewportIdVolumeInputArrayMap // copy the viewportIdVolumeInputArrayMap
const viewportIdVolumeInputArrayMapCopy = new Map( const viewportIdVolumeInputArrayMapCopy = new Map(viewportIdVolumeInputArrayMap);
viewportIdVolumeInputArrayMap
);
// reset the viewportIdVolumeInputArrayMap // reset the viewportIdVolumeInputArrayMap
viewportIdVolumeInputArrayMap.clear(); viewportIdVolumeInputArrayMap.clear();

View File

@ -32,11 +32,7 @@ const Angle = {
throw new Error('Tool not supported'); throw new Error('Tool not supported');
} }
const { const { SOPInstanceUID, SeriesInstanceUID, StudyInstanceUID } = getSOPInstanceAttributes(
SOPInstanceUID,
SeriesInstanceUID,
StudyInstanceUID,
} = getSOPInstanceAttributes(
referencedImageId, referencedImageId,
CornerstoneViewportService, CornerstoneViewportService,
viewportId viewportId
@ -55,14 +51,10 @@ const Angle = {
const { points } = data.handles; const { points } = data.handles;
const mappedAnnotations = getMappedAnnotations( const mappedAnnotations = getMappedAnnotations(annotation, displaySetService);
annotation,
displaySetService
);
const displayText = getDisplayText(mappedAnnotations, displaySet); const displayText = getDisplayText(mappedAnnotations, displaySet);
const getReport = () => const getReport = () => _getReport(mappedAnnotations, points, FrameOfReferenceUID);
_getReport(mappedAnnotations, points, FrameOfReferenceUID);
return { return {
uid: annotationUID, uid: annotationUID,
@ -99,16 +91,11 @@ function getMappedAnnotations(annotation, DisplaySetService) {
const targetStats = cachedStats[targetId]; const targetStats = cachedStats[targetId];
if (!referencedImageId) { if (!referencedImageId) {
throw new Error( throw new Error('Non-acquisition plane measurement mapping not supported');
'Non-acquisition plane measurement mapping not supported'
);
} }
const { const { SOPInstanceUID, SeriesInstanceUID, frameNumber } =
SOPInstanceUID, getSOPInstanceAttributes(referencedImageId);
SeriesInstanceUID,
frameNumber,
} = getSOPInstanceAttributes(referencedImageId);
const displaySet = DisplaySetService.getDisplaySetForSOPInstanceUID( const displaySet = DisplaySetService.getDisplaySetForSOPInstanceUID(
SOPInstanceUID, SOPInstanceUID,
@ -179,17 +166,9 @@ function getDisplayText(mappedAnnotations, displaySet) {
const displayText = []; const displayText = [];
// Area is the same for all series // Area is the same for all series
const { const { angle, unit, SeriesNumber, SOPInstanceUID, frameNumber } = mappedAnnotations[0];
angle,
unit,
SeriesNumber,
SOPInstanceUID,
frameNumber,
} = mappedAnnotations[0];
const instance = displaySet.images.find( const instance = displaySet.images.find(image => image.SOPInstanceUID === SOPInstanceUID);
image => image.SOPInstanceUID === SOPInstanceUID
);
let InstanceNumber; let InstanceNumber;
if (instance) { if (instance) {
@ -202,9 +181,7 @@ function getDisplayText(mappedAnnotations, displaySet) {
return displayText; return displayText;
} }
const roundedAngle = utils.roundNumber(angle, 2); const roundedAngle = utils.roundNumber(angle, 2);
displayText.push( displayText.push(`${roundedAngle} ${unit} (S: ${SeriesNumber}${instanceText}${frameText})`);
`${roundedAngle} ${unit} (S: ${SeriesNumber}${instanceText}${frameText})`
);
return displayText; return displayText;
} }

View File

@ -31,11 +31,7 @@ const Length = {
throw new Error('Tool not supported'); throw new Error('Tool not supported');
} }
const { const { SOPInstanceUID, SeriesInstanceUID, StudyInstanceUID } = getSOPInstanceAttributes(
SOPInstanceUID,
SeriesInstanceUID,
StudyInstanceUID,
} = getSOPInstanceAttributes(
referencedImageId, referencedImageId,
cornerstoneViewportService, cornerstoneViewportService,
viewportId viewportId
@ -54,10 +50,7 @@ const Length = {
const { points } = data.handles; const { points } = data.handles;
const mappedAnnotations = getMappedAnnotations( const mappedAnnotations = getMappedAnnotations(annotation, displaySetService);
annotation,
displaySetService
);
const displayText = getDisplayText(mappedAnnotations, displaySet); const displayText = getDisplayText(mappedAnnotations, displaySet);
@ -91,11 +84,8 @@ function getMappedAnnotations(annotation, displaySetService) {
const annotations = []; const annotations = [];
const { const { SOPInstanceUID, SeriesInstanceUID, frameNumber } =
SOPInstanceUID, getSOPInstanceAttributes(referencedImageId);
SeriesInstanceUID,
frameNumber,
} = getSOPInstanceAttributes(referencedImageId);
const displaySet = displaySetService.getDisplaySetForSOPInstanceUID( const displaySet = displaySetService.getDisplaySetForSOPInstanceUID(
SOPInstanceUID, SOPInstanceUID,
@ -126,9 +116,7 @@ function getDisplayText(mappedAnnotations, displaySet) {
// Area is the same for all series // Area is the same for all series
const { SeriesNumber, SOPInstanceUID, frameNumber } = mappedAnnotations[0]; const { SeriesNumber, SOPInstanceUID, frameNumber } = mappedAnnotations[0];
const instance = displaySet.images.find( const instance = displaySet.images.find(image => image.SOPInstanceUID === SOPInstanceUID);
image => image.SOPInstanceUID === SOPInstanceUID
);
let InstanceNumber; let InstanceNumber;
if (instance) { if (instance) {

View File

@ -27,11 +27,7 @@ const Bidirectional = {
throw new Error('Tool not supported'); throw new Error('Tool not supported');
} }
const { const { SOPInstanceUID, SeriesInstanceUID, StudyInstanceUID } = getSOPInstanceAttributes(
SOPInstanceUID,
SeriesInstanceUID,
StudyInstanceUID,
} = getSOPInstanceAttributes(
referencedImageId, referencedImageId,
cornerstoneViewportService, cornerstoneViewportService,
viewportId viewportId
@ -50,14 +46,10 @@ const Bidirectional = {
const { points } = data.handles; const { points } = data.handles;
const mappedAnnotations = getMappedAnnotations( const mappedAnnotations = getMappedAnnotations(annotation, displaySetService);
annotation,
displaySetService
);
const displayText = getDisplayText(mappedAnnotations, displaySet); const displayText = getDisplayText(mappedAnnotations, displaySet);
const getReport = () => const getReport = () => _getReport(mappedAnnotations, points, FrameOfReferenceUID);
_getReport(mappedAnnotations, points, FrameOfReferenceUID);
return { return {
uid: annotationUID, uid: annotationUID,
@ -94,16 +86,11 @@ function getMappedAnnotations(annotation, displaySetService) {
const targetStats = cachedStats[targetId]; const targetStats = cachedStats[targetId];
if (!referencedImageId) { if (!referencedImageId) {
throw new Error( throw new Error('Non-acquisition plane measurement mapping not supported');
'Non-acquisition plane measurement mapping not supported'
);
} }
const { const { SOPInstanceUID, SeriesInstanceUID, frameNumber } =
SOPInstanceUID, getSOPInstanceAttributes(referencedImageId);
SeriesInstanceUID,
frameNumber,
} = getSOPInstanceAttributes(referencedImageId);
const displaySet = displaySetService.getDisplaySetForSOPInstanceUID( const displaySet = displaySetService.getDisplaySetForSOPInstanceUID(
SOPInstanceUID, SOPInstanceUID,
@ -175,19 +162,11 @@ function getDisplayText(mappedAnnotations, displaySet) {
const displayText = []; const displayText = [];
// Area is the same for all series // Area is the same for all series
const { const { length, width, SeriesNumber, SOPInstanceUID, frameNumber } = mappedAnnotations[0];
length,
width,
SeriesNumber,
SOPInstanceUID,
frameNumber,
} = mappedAnnotations[0];
const roundedLength = utils.roundNumber(length, 2); const roundedLength = utils.roundNumber(length, 2);
const roundedWidth = utils.roundNumber(width, 2); const roundedWidth = utils.roundNumber(width, 2);
const instance = displaySet.images.find( const instance = displaySet.images.find(image => image.SOPInstanceUID === SOPInstanceUID);
image => image.SOPInstanceUID === SOPInstanceUID
);
let InstanceNumber; let InstanceNumber;
if (instance) { if (instance) {
@ -197,9 +176,7 @@ function getDisplayText(mappedAnnotations, displaySet) {
const instanceText = InstanceNumber ? ` I: ${InstanceNumber}` : ''; const instanceText = InstanceNumber ? ` I: ${InstanceNumber}` : '';
const frameText = displaySet.isMultiFrame ? ` F: ${frameNumber}` : ''; const frameText = displaySet.isMultiFrame ? ` F: ${frameNumber}` : '';
displayText.push( displayText.push(`L: ${roundedLength} mm (S: ${SeriesNumber}${instanceText}${frameText})`);
`L: ${roundedLength} mm (S: ${SeriesNumber}${instanceText}${frameText})`
);
displayText.push(`W: ${roundedWidth} mm`); displayText.push(`W: ${roundedWidth} mm`);
return displayText; return displayText;

View File

@ -25,11 +25,7 @@ const CircleROI = {
throw new Error('Tool not supported'); throw new Error('Tool not supported');
} }
const { const { SOPInstanceUID, SeriesInstanceUID, StudyInstanceUID } = getSOPInstanceAttributes(
SOPInstanceUID,
SeriesInstanceUID,
StudyInstanceUID,
} = getSOPInstanceAttributes(
referencedImageId, referencedImageId,
CornerstoneViewportService, CornerstoneViewportService,
viewportId viewportId
@ -48,14 +44,10 @@ const CircleROI = {
const { points } = data.handles; const { points } = data.handles;
const mappedAnnotations = getMappedAnnotations( const mappedAnnotations = getMappedAnnotations(annotation, DisplaySetService);
annotation,
DisplaySetService
);
const displayText = getDisplayText(mappedAnnotations, displaySet); const displayText = getDisplayText(mappedAnnotations, displaySet);
const getReport = () => const getReport = () => _getReport(mappedAnnotations, points, FrameOfReferenceUID);
_getReport(mappedAnnotations, points, FrameOfReferenceUID);
return { return {
uid: annotationUID, uid: annotationUID,
@ -93,16 +85,11 @@ function getMappedAnnotations(annotation, DisplaySetService) {
if (!referencedImageId) { if (!referencedImageId) {
// Todo: Non-acquisition plane measurement mapping not supported yet // Todo: Non-acquisition plane measurement mapping not supported yet
throw new Error( throw new Error('Non-acquisition plane measurement mapping not supported');
'Non-acquisition plane measurement mapping not supported'
);
} }
const { const { SOPInstanceUID, SeriesInstanceUID, frameNumber } =
SOPInstanceUID, getSOPInstanceAttributes(referencedImageId);
SeriesInstanceUID,
frameNumber,
} = getSOPInstanceAttributes(referencedImageId);
const displaySet = DisplaySetService.getDisplaySetForSOPInstanceUID( const displaySet = DisplaySetService.getDisplaySetForSOPInstanceUID(
SOPInstanceUID, SOPInstanceUID,
@ -150,12 +137,7 @@ function _getReport(mappedAnnotations, points, FrameOfReferenceUID) {
return; return;
} }
columns.push( columns.push(`max (${unit})`, `mean (${unit})`, `std (${unit})`, `area (mm2)`);
`max (${unit})`,
`mean (${unit})`,
`std (${unit})`,
`area (mm2)`
);
values.push(max, mean, stdDev, area); values.push(max, mean, stdDev, area);
}); });
@ -188,9 +170,7 @@ function getDisplayText(mappedAnnotations, displaySet) {
// Area is the same for all series // Area is the same for all series
const { area, SOPInstanceUID, frameNumber } = mappedAnnotations[0]; const { area, SOPInstanceUID, frameNumber } = mappedAnnotations[0];
const instance = displaySet.images.find( const instance = displaySet.images.find(image => image.SOPInstanceUID === SOPInstanceUID);
image => image.SOPInstanceUID === SOPInstanceUID
);
let InstanceNumber; let InstanceNumber;
if (instance) { if (instance) {

View File

@ -32,11 +32,7 @@ const CobbAngle = {
throw new Error('Tool not supported'); throw new Error('Tool not supported');
} }
const { const { SOPInstanceUID, SeriesInstanceUID, StudyInstanceUID } = getSOPInstanceAttributes(
SOPInstanceUID,
SeriesInstanceUID,
StudyInstanceUID,
} = getSOPInstanceAttributes(
referencedImageId, referencedImageId,
CornerstoneViewportService, CornerstoneViewportService,
viewportId viewportId
@ -55,14 +51,10 @@ const CobbAngle = {
const { points } = data.handles; const { points } = data.handles;
const mappedAnnotations = getMappedAnnotations( const mappedAnnotations = getMappedAnnotations(annotation, displaySetService);
annotation,
displaySetService
);
const displayText = getDisplayText(mappedAnnotations, displaySet); const displayText = getDisplayText(mappedAnnotations, displaySet);
const getReport = () => const getReport = () => _getReport(mappedAnnotations, points, FrameOfReferenceUID);
_getReport(mappedAnnotations, points, FrameOfReferenceUID);
return { return {
uid: annotationUID, uid: annotationUID,
@ -99,16 +91,11 @@ function getMappedAnnotations(annotation, DisplaySetService) {
const targetStats = cachedStats[targetId]; const targetStats = cachedStats[targetId];
if (!referencedImageId) { if (!referencedImageId) {
throw new Error( throw new Error('Non-acquisition plane measurement mapping not supported');
'Non-acquisition plane measurement mapping not supported'
);
} }
const { const { SOPInstanceUID, SeriesInstanceUID, frameNumber } =
SOPInstanceUID, getSOPInstanceAttributes(referencedImageId);
SeriesInstanceUID,
frameNumber,
} = getSOPInstanceAttributes(referencedImageId);
const displaySet = DisplaySetService.getDisplaySetForSOPInstanceUID( const displaySet = DisplaySetService.getDisplaySetForSOPInstanceUID(
SOPInstanceUID, SOPInstanceUID,
@ -179,17 +166,9 @@ function getDisplayText(mappedAnnotations, displaySet) {
const displayText = []; const displayText = [];
// Area is the same for all series // Area is the same for all series
const { const { angle, unit, SeriesNumber, SOPInstanceUID, frameNumber } = mappedAnnotations[0];
angle,
unit,
SeriesNumber,
SOPInstanceUID,
frameNumber,
} = mappedAnnotations[0];
const instance = displaySet.images.find( const instance = displaySet.images.find(image => image.SOPInstanceUID === SOPInstanceUID);
image => image.SOPInstanceUID === SOPInstanceUID
);
let InstanceNumber; let InstanceNumber;
if (instance) { if (instance) {
@ -202,9 +181,7 @@ function getDisplayText(mappedAnnotations, displaySet) {
return displayText; return displayText;
} }
const roundedAngle = utils.roundNumber(angle, 2); const roundedAngle = utils.roundNumber(angle, 2);
displayText.push( displayText.push(`${roundedAngle} ${unit} (S: ${SeriesNumber}${instanceText}${frameText})`);
`${roundedAngle} ${unit} (S: ${SeriesNumber}${instanceText}${frameText})`
);
return displayText; return displayText;
} }

View File

@ -25,11 +25,7 @@ const EllipticalROI = {
throw new Error('Tool not supported'); throw new Error('Tool not supported');
} }
const { const { SOPInstanceUID, SeriesInstanceUID, StudyInstanceUID } = getSOPInstanceAttributes(
SOPInstanceUID,
SeriesInstanceUID,
StudyInstanceUID,
} = getSOPInstanceAttributes(
referencedImageId, referencedImageId,
cornerstoneViewportService, cornerstoneViewportService,
viewportId viewportId
@ -48,14 +44,10 @@ const EllipticalROI = {
const { points } = data.handles; const { points } = data.handles;
const mappedAnnotations = getMappedAnnotations( const mappedAnnotations = getMappedAnnotations(annotation, displaySetService);
annotation,
displaySetService
);
const displayText = getDisplayText(mappedAnnotations, displaySet); const displayText = getDisplayText(mappedAnnotations, displaySet);
const getReport = () => const getReport = () => _getReport(mappedAnnotations, points, FrameOfReferenceUID);
_getReport(mappedAnnotations, points, FrameOfReferenceUID);
return { return {
uid: annotationUID, uid: annotationUID,
@ -93,16 +85,11 @@ function getMappedAnnotations(annotation, displaySetService) {
if (!referencedImageId) { if (!referencedImageId) {
// Todo: Non-acquisition plane measurement mapping not supported yet // Todo: Non-acquisition plane measurement mapping not supported yet
throw new Error( throw new Error('Non-acquisition plane measurement mapping not supported');
'Non-acquisition plane measurement mapping not supported'
);
} }
const { const { SOPInstanceUID, SeriesInstanceUID, frameNumber } =
SOPInstanceUID, getSOPInstanceAttributes(referencedImageId);
SeriesInstanceUID,
frameNumber,
} = getSOPInstanceAttributes(referencedImageId);
const displaySet = displaySetService.getDisplaySetForSOPInstanceUID( const displaySet = displaySetService.getDisplaySetForSOPInstanceUID(
SOPInstanceUID, SOPInstanceUID,
@ -150,12 +137,7 @@ function _getReport(mappedAnnotations, points, FrameOfReferenceUID) {
return; return;
} }
columns.push( columns.push(`max (${unit})`, `mean (${unit})`, `std (${unit})`, `area (mm2)`);
`max (${unit})`,
`mean (${unit})`,
`std (${unit})`,
`area (mm2)`
);
values.push(max, mean, stdDev, area); values.push(max, mean, stdDev, area);
}); });
@ -188,9 +170,7 @@ function getDisplayText(mappedAnnotations, displaySet) {
// Area is the same for all series // Area is the same for all series
const { area, SOPInstanceUID, frameNumber } = mappedAnnotations[0]; const { area, SOPInstanceUID, frameNumber } = mappedAnnotations[0];
const instance = displaySet.images.find( const instance = displaySet.images.find(image => image.SOPInstanceUID === SOPInstanceUID);
image => image.SOPInstanceUID === SOPInstanceUID
);
let InstanceNumber; let InstanceNumber;
if (instance) { if (instance) {

View File

@ -32,11 +32,7 @@ const Length = {
throw new Error('Tool not supported'); throw new Error('Tool not supported');
} }
const { const { SOPInstanceUID, SeriesInstanceUID, StudyInstanceUID } = getSOPInstanceAttributes(
SOPInstanceUID,
SeriesInstanceUID,
StudyInstanceUID,
} = getSOPInstanceAttributes(
referencedImageId, referencedImageId,
cornerstoneViewportService, cornerstoneViewportService,
viewportId viewportId
@ -55,14 +51,10 @@ const Length = {
const { points } = data.handles; const { points } = data.handles;
const mappedAnnotations = getMappedAnnotations( const mappedAnnotations = getMappedAnnotations(annotation, displaySetService);
annotation,
displaySetService
);
const displayText = getDisplayText(mappedAnnotations, displaySet); const displayText = getDisplayText(mappedAnnotations, displaySet);
const getReport = () => const getReport = () => _getReport(mappedAnnotations, points, FrameOfReferenceUID);
_getReport(mappedAnnotations, points, FrameOfReferenceUID);
return { return {
uid: annotationUID, uid: annotationUID,
@ -99,16 +91,11 @@ function getMappedAnnotations(annotation, displaySetService) {
const targetStats = cachedStats[targetId]; const targetStats = cachedStats[targetId];
if (!referencedImageId) { if (!referencedImageId) {
throw new Error( throw new Error('Non-acquisition plane measurement mapping not supported');
'Non-acquisition plane measurement mapping not supported'
);
} }
const { const { SOPInstanceUID, SeriesInstanceUID, frameNumber } =
SOPInstanceUID, getSOPInstanceAttributes(referencedImageId);
SeriesInstanceUID,
frameNumber,
} = getSOPInstanceAttributes(referencedImageId);
const displaySet = displaySetService.getDisplaySetForSOPInstanceUID( const displaySet = displaySetService.getDisplaySetForSOPInstanceUID(
SOPInstanceUID, SOPInstanceUID,
@ -179,16 +166,9 @@ function getDisplayText(mappedAnnotations, displaySet) {
const displayText = []; const displayText = [];
// Area is the same for all series // Area is the same for all series
const { const { length, SeriesNumber, SOPInstanceUID, frameNumber } = mappedAnnotations[0];
length,
SeriesNumber,
SOPInstanceUID,
frameNumber,
} = mappedAnnotations[0];
const instance = displaySet.images.find( const instance = displaySet.images.find(image => image.SOPInstanceUID === SOPInstanceUID);
image => image.SOPInstanceUID === SOPInstanceUID
);
let InstanceNumber; let InstanceNumber;
if (instance) { if (instance) {
@ -202,9 +182,7 @@ function getDisplayText(mappedAnnotations, displaySet) {
return displayText; return displayText;
} }
const roundedLength = utils.roundNumber(length, 2); const roundedLength = utils.roundNumber(length, 2);
displayText.push( displayText.push(`${roundedLength} mm (S: ${SeriesNumber}${instanceText}${frameText})`);
`${roundedLength} mm (S: ${SeriesNumber}${instanceText}${frameText})`
);
return displayText; return displayText;
} }

Some files were not shown because too many files have changed in this diff Show More