chore(prettier): full repo linting and code reformatting once and for all (#3627)
This commit is contained in:
parent
01500a2d4c
commit
9045ddaedc
@ -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" }}'
|
||||||
|
|||||||
@ -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",
|
||||||
|
|||||||
12
.github/ISSUE_TEMPLATE/bug-report.yml
vendored
12
.github/ISSUE_TEMPLATE/bug-report.yml
vendored
@ -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.
|
||||||
|
|||||||
8
.github/ISSUE_TEMPLATE/feature-request.yml
vendored
8
.github/ISSUE_TEMPLATE/feature-request.yml
vendored
@ -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
5
.github/stale.yml
vendored
@ -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
|
||||||
|
|||||||
@ -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>
|
||||||
|
|||||||
@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
@ -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';
|
||||||
|
|
||||||
|
|||||||
@ -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'],
|
||||||
|
|||||||
@ -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',
|
||||||
|
|||||||
@ -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 = () => [
|
||||||
|
|||||||
@ -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,
|
||||||
|
|||||||
@ -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__'],
|
||||||
|
|||||||
@ -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
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|||||||
@ -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 => {
|
||||||
|
|||||||
@ -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,
|
||||||
|
|||||||
@ -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;
|
||||||
|
|
||||||
|
|||||||
@ -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, {});
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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 = [
|
||||||
{
|
{
|
||||||
|
|||||||
@ -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..."
|
||||||
|
|||||||
@ -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>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@ -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,
|
||||||
|
|||||||
@ -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__'],
|
||||||
|
|||||||
@ -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 [
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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}
|
||||||
|
|||||||
@ -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 => {
|
||||||
|
|||||||
@ -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;
|
||||||
|
|
||||||
|
|||||||
@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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, {});
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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 = [
|
||||||
{
|
{
|
||||||
|
|||||||
@ -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..."
|
||||||
|
|||||||
@ -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>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@ -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,
|
||||||
|
|||||||
@ -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.'
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@ -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 = [];
|
||||||
|
|||||||
@ -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 => {
|
||||||
|
|||||||
@ -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);
|
||||||
|
|||||||
@ -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,
|
||||||
|
|||||||
@ -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(
|
||||||
|
|||||||
@ -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');
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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,
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
@ -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);
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
@ -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];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -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');
|
||||||
|
|||||||
@ -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>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@ -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,
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@ -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>
|
||||||
|
|||||||
@ -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);
|
||||||
|
|||||||
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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);
|
||||||
|
|||||||
@ -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 =>
|
||||||
|
|||||||
@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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>
|
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -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}`}
|
{`${numFilesCompleted} of ${dicomFileUploaderArr.length}`}
|
||||||
</span>
|
</span>
|
||||||
|
<span className={NO_WRAP_ELLIPSIS_CLASS_NAMES}>{' files completed.'} </span>
|
||||||
<span className={NO_WRAP_ELLIPSIS_CLASS_NAMES}>
|
<span className={NO_WRAP_ELLIPSIS_CLASS_NAMES}>
|
||||||
{' files completed.'}
|
{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,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -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}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
@ -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>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
@ -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 });
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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,
|
||||||
|
|||||||
@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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}`,
|
||||||
};
|
};
|
||||||
}
|
});
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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);
|
||||||
|
|||||||
@ -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);
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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] = {
|
||||||
|
|||||||
@ -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) => {
|
||||||
|
|||||||
@ -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,
|
|
||||||
}
|
|
||||||
|
|||||||
@ -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);
|
||||||
|
|||||||
@ -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) {
|
||||||
|
|||||||
@ -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}
|
||||||
|
|||||||
@ -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();
|
||||||
|
|||||||
@ -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);
|
||||||
|
|||||||
@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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:
|
||||||
|
|||||||
@ -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`
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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();
|
||||||
|
|||||||
@ -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();
|
||||||
|
|||||||
@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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) {
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
@ -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) {
|
||||||
|
|||||||
@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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) {
|
||||||
|
|||||||
@ -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
Loading…
Reference in New Issue
Block a user