Compare commits
118
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ea0963d75f | ||
|
|
46d5718011 | ||
|
|
f8740e04f3 | ||
|
|
669717d27b | ||
|
|
715dfc556a | ||
|
|
cafb7d3f7b | ||
|
|
5f92f6ec7f | ||
|
|
eea610dfe8 | ||
|
|
e8733a9bd9 | ||
|
|
d292931073 | ||
|
|
f6a0db753a | ||
|
|
8518e179ee | ||
|
|
3f40cdced7 | ||
|
|
4cf17185da | ||
|
|
b8ae92c2d4 | ||
|
|
bad67020cc | ||
|
|
2afcd498f3 | ||
|
|
ce4c3d5360 | ||
|
|
d575e36879 | ||
|
|
98fcabfcb5 | ||
|
|
1e2773345a | ||
|
|
f847633cf3 | ||
|
|
bd2ddd7702 | ||
|
|
26b7b34bd4 | ||
|
|
6f3e446a79 | ||
|
|
f0d7f2e6c7 | ||
|
|
c8f7abfd53 | ||
|
|
2b29ff1820 | ||
|
|
5627e9c919 | ||
|
|
582b713b84 | ||
|
|
5eb8e58b65 | ||
|
|
403688b18c | ||
|
|
f0d13a7acd | ||
|
|
78f1a7f3e5 | ||
|
|
a57ab4d12a | ||
|
|
3b6c6374e3 | ||
|
|
4b508b0c25 | ||
|
|
1cbef867ce | ||
|
|
05444f48d4 | ||
|
|
2ea227ca06 | ||
|
|
35aaad175c | ||
|
|
863fe05907 | ||
|
|
1c9560044f | ||
|
|
343f06c57d | ||
|
|
e2bb647386 | ||
|
|
a3cb9dab57 | ||
|
|
410ab028c7 | ||
|
|
54dd738f61 | ||
|
|
6f16c7e48d | ||
|
|
82ddd95cf0 | ||
|
|
2b658fae7e | ||
|
|
a822151e92 | ||
|
|
0ae49c6fdc | ||
|
|
1d069d2da3 | ||
|
|
274342a5e1 | ||
|
|
21a5d1bfc4 | ||
|
|
e421bc7df9 | ||
|
|
e41ba2bfde | ||
|
|
d2e75b0f20 | ||
|
|
07b272e024 | ||
|
|
a08caa9bdb | ||
|
|
29afddb3a6 | ||
|
|
f85f856734 | ||
|
|
c6bc19c002 | ||
|
|
538555f000 | ||
|
|
8681036b21 | ||
|
|
e55b909ca6 | ||
|
|
4bb36704ee | ||
|
|
9bbdb35d88 | ||
|
|
2f443fa2ef | ||
|
|
6d7d7da287 | ||
|
|
1f6751f95b | ||
|
|
92081a097d | ||
|
|
ba1a33c3ac | ||
|
|
193ff12b6e | ||
|
|
3296ffaa07 | ||
|
|
0456d92462 | ||
|
|
db3b15e537 | ||
|
|
6e723ebced | ||
|
|
a6a231c81e | ||
|
|
7c2bd9b76a | ||
|
|
5dfa8d465d | ||
|
|
fc5301e904 | ||
|
|
88e5a7e54a | ||
|
|
5d24bb4b73 | ||
|
|
03a4056179 | ||
|
|
ec76bc3b0b | ||
|
|
b1070acaca | ||
|
|
7b5beb8f98 | ||
|
|
97410e12b9 | ||
|
|
bdecbfeee3 | ||
|
|
5a970056ae | ||
|
|
8581405352 | ||
|
|
df8c4db2ed | ||
|
|
ea62bdaddf | ||
|
|
e983b02677 | ||
|
|
1e4b78d78d | ||
|
|
ee6131f98e | ||
|
|
a56bdd035b | ||
|
|
04901c296e | ||
|
|
59cadad98c | ||
|
|
14d6454eaf | ||
|
|
64f87d2c93 | ||
|
|
cfd66e5ef7 | ||
|
|
6480389778 | ||
|
|
0e736e7a56 | ||
|
|
d246cd9842 | ||
|
|
4fa1eb683d | ||
|
|
ea6842a276 | ||
|
|
d97270f995 | ||
|
|
0ef76f8b66 | ||
|
|
05b5a10af9 | ||
|
|
e1150acaf6 | ||
|
|
4abdbe0584 | ||
|
|
9223a52af9 | ||
|
|
06f58d18bf | ||
|
|
7c5f9c44b6 | ||
|
|
ef341d1518 |
No files matched your search
+35
-27
@@ -48,12 +48,12 @@ jobs:
|
|||||||
- checkout:
|
- checkout:
|
||||||
post:
|
post:
|
||||||
- git fetch --all
|
- git fetch --all
|
||||||
- restore_cache:
|
# - restore_cache:
|
||||||
name: Restore Yarn and Cypress Package Cache
|
# name: Restore Yarn and Cypress Package Cache
|
||||||
keys:
|
# keys:
|
||||||
# when lock file changes, use increasingly general patterns to restore cache
|
# # when lock file changes, use increasingly general patterns to restore cache
|
||||||
- yarn-packages-{{ checksum "yarn.lock" }}
|
# - yarn-packages-{{ checksum "yarn.lock" }}
|
||||||
- yarn-packages-
|
# - yarn-packages-
|
||||||
- run:
|
- run:
|
||||||
name: Install Dependencies
|
name: Install Dependencies
|
||||||
command: yarn install --frozen-lockfile
|
command: yarn install --frozen-lockfile
|
||||||
@@ -107,12 +107,12 @@ jobs:
|
|||||||
post:
|
post:
|
||||||
- git fetch --all
|
- git fetch --all
|
||||||
|
|
||||||
- restore_cache:
|
# - restore_cache:
|
||||||
name: Restore Yarn and Cypress Package Cache
|
# name: Restore Yarn and Cypress Package Cache
|
||||||
keys:
|
# keys:
|
||||||
# when lock file changes, use increasingly general patterns to restore cache
|
# # when lock file changes, use increasingly general patterns to restore cache
|
||||||
- yarn-packages-{{ checksum "yarn.lock" }}
|
# - yarn-packages-{{ checksum "yarn.lock" }}
|
||||||
- yarn-packages-
|
# - yarn-packages-
|
||||||
|
|
||||||
- run:
|
- run:
|
||||||
name: Install Dependencies
|
name: Install Dependencies
|
||||||
@@ -143,12 +143,12 @@ jobs:
|
|||||||
- checkout:
|
- checkout:
|
||||||
post:
|
post:
|
||||||
- git fetch --all
|
- git fetch --all
|
||||||
- restore_cache:
|
# - restore_cache:
|
||||||
name: Restore Yarn and Cypress Package Cache
|
# name: Restore Yarn and Cypress Package Cache
|
||||||
keys:
|
# keys:
|
||||||
# when lock file changes, use increasingly general patterns to restore cache
|
# # when lock file changes, use increasingly general patterns to restore cache
|
||||||
- yarn-packages-{{ checksum "yarn.lock" }}
|
# - yarn-packages-{{ checksum "yarn.lock" }}
|
||||||
- yarn-packages-
|
# - yarn-packages-
|
||||||
- run:
|
- run:
|
||||||
name: Install Dependencies
|
name: Install Dependencies
|
||||||
command: yarn install --frozen-lockfile
|
command: yarn install --frozen-lockfile
|
||||||
@@ -237,11 +237,11 @@ jobs:
|
|||||||
post:
|
post:
|
||||||
- git fetch --all
|
- git fetch --all
|
||||||
# Use increasingly general patterns to restore cache
|
# Use increasingly general patterns to restore cache
|
||||||
- restore_cache:
|
# - restore_cache:
|
||||||
name: Restore Yarn and Cypress Package Cache
|
# name: Restore Yarn and Cypress Package Cache
|
||||||
keys:
|
# keys:
|
||||||
- yarn-packages-{{ checksum "yarn.lock" }}
|
# - yarn-packages-{{ checksum "yarn.lock" }}
|
||||||
- yarn-packages-
|
# - yarn-packages-
|
||||||
- run:
|
- run:
|
||||||
name: Install Dependencies
|
name: Install Dependencies
|
||||||
command: yarn install --frozen-lockfile
|
command: yarn install --frozen-lockfile
|
||||||
@@ -346,7 +346,9 @@ workflows:
|
|||||||
record: true
|
record: true
|
||||||
store_artifacts: true
|
store_artifacts: true
|
||||||
working_directory: platform/viewer
|
working_directory: platform/viewer
|
||||||
build: npx cross-env QUICK_BUILD=true APP_CONFIG=config/dicomweb-server.js yarn run build
|
build:
|
||||||
|
npx cross-env QUICK_BUILD=true APP_CONFIG=config/dicomweb-server.js
|
||||||
|
yarn run build
|
||||||
start: yarn run test:e2e:serve
|
start: yarn run test:e2e:serve
|
||||||
spec: 'cypress/integration/common/**/*,cypress/integration/pwa/**/*'
|
spec: 'cypress/integration/common/**/*,cypress/integration/pwa/**/*'
|
||||||
wait-on: 'http://localhost:3000'
|
wait-on: 'http://localhost:3000'
|
||||||
@@ -372,7 +374,9 @@ workflows:
|
|||||||
record: true
|
record: true
|
||||||
store_artifacts: true
|
store_artifacts: true
|
||||||
working_directory: platform/viewer
|
working_directory: platform/viewer
|
||||||
build: npx cross-env QUICK_BUILD=true APP_CONFIG=config/dicomweb-server.js yarn run build:package
|
build:
|
||||||
|
npx cross-env QUICK_BUILD=true APP_CONFIG=config/dicomweb-server.js
|
||||||
|
yarn run build:package
|
||||||
start: yarn run test:e2e:serve
|
start: yarn run test:e2e:serve
|
||||||
spec: 'cypress/integration/common/**/*,cypress/integration/script-tag/**/*'
|
spec: 'cypress/integration/common/**/*,cypress/integration/script-tag/**/*'
|
||||||
wait-on: 'http://localhost:3000'
|
wait-on: 'http://localhost:3000'
|
||||||
@@ -403,7 +407,9 @@ workflows:
|
|||||||
yarn: true
|
yarn: true
|
||||||
store_artifacts: false
|
store_artifacts: false
|
||||||
working_directory: platform/viewer
|
working_directory: platform/viewer
|
||||||
build: npx cross-env QUICK_BUILD=true APP_CONFIG=config/dicomweb-server.js yarn run build
|
build:
|
||||||
|
npx cross-env QUICK_BUILD=true APP_CONFIG=config/dicomweb-server.js
|
||||||
|
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" }}'
|
||||||
@@ -480,7 +486,9 @@ workflows:
|
|||||||
yarn: true
|
yarn: true
|
||||||
store_artifacts: false
|
store_artifacts: false
|
||||||
working_directory: platform/viewer
|
working_directory: platform/viewer
|
||||||
build: npx cross-env QUICK_BUILD=true APP_CONFIG=config/dicomweb-server.js yarn run build
|
build:
|
||||||
|
npx cross-env QUICK_BUILD=true APP_CONFIG=config/dicomweb-server.js
|
||||||
|
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" }}'
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ src/version.js
|
|||||||
junit.xml
|
junit.xml
|
||||||
coverage/
|
coverage/
|
||||||
.docz/
|
.docz/
|
||||||
|
*.js.map
|
||||||
|
|
||||||
# YALC (for Erik)
|
# YALC (for Erik)
|
||||||
.yalc
|
.yalc
|
||||||
@@ -34,3 +35,4 @@ screenshots/
|
|||||||
|
|
||||||
# Locize settings
|
# Locize settings
|
||||||
.locize
|
.locize
|
||||||
|
|
||||||
Vendored
+15
@@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
// Use IntelliSense to learn about possible attributes.
|
||||||
|
// Hover to view descriptions of existing attributes.
|
||||||
|
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||||
|
"version": "0.2.0",
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"type": "pwa-chrome",
|
||||||
|
"request": "launch",
|
||||||
|
"name": "Launch Chrome against localhost",
|
||||||
|
"url": "http://localhost:3000",
|
||||||
|
"webRoot": "${workspaceFolder}"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
cff-version: 1.2.0
|
||||||
|
message: "If you use this software, please cite it as below."
|
||||||
|
authors:
|
||||||
|
- family-names: "Ziegler"
|
||||||
|
given-names: "Erik"
|
||||||
|
orcid: "https://orcid.org/0000-0003-1857-8129"
|
||||||
|
- family-names: "Urban"
|
||||||
|
given-names: "Trinity"
|
||||||
|
- family-names: "Brown"
|
||||||
|
given-names: "Danny"
|
||||||
|
- family-names: "Petts"
|
||||||
|
given-names: "James"
|
||||||
|
- family-names: "Pieper"
|
||||||
|
given-names: "Steve D"
|
||||||
|
- family-names: "Lewis"
|
||||||
|
given-names: "Rob"
|
||||||
|
- family-names: "Hafey"
|
||||||
|
given-names: "Chris"
|
||||||
|
- family-names: "Harris"
|
||||||
|
given-names: "Gordon J"
|
||||||
|
title: "Open Health Imaging Foundation Viewer: An Extensible Open-Source Framework for Building Web-Based Imaging Applications to Support Cancer Research"
|
||||||
|
doi: 10.1200/CCI.19.00131
|
||||||
|
version: 1.0.0
|
||||||
|
url: "https://github.com/OHIF/Viewers"
|
||||||
+3
-2
@@ -21,7 +21,7 @@
|
|||||||
|
|
||||||
# Stage 1: Build the application
|
# Stage 1: Build the application
|
||||||
# docker build -t ohif/viewer:latest .
|
# docker build -t ohif/viewer:latest .
|
||||||
FROM node:10.16.3-slim as builder
|
FROM node:15.13.0-slim as builder
|
||||||
|
|
||||||
RUN mkdir /usr/src/app
|
RUN mkdir /usr/src/app
|
||||||
WORKDIR /usr/src/app
|
WORKDIR /usr/src/app
|
||||||
@@ -39,6 +39,7 @@ COPY package.json /usr/src/app/package.json
|
|||||||
COPY postcss.config.js /usr/src/app/postcss.config.js
|
COPY postcss.config.js /usr/src/app/postcss.config.js
|
||||||
COPY yarn.lock /usr/src/app/yarn.lock
|
COPY yarn.lock /usr/src/app/yarn.lock
|
||||||
|
|
||||||
|
RUN apt-get update && apt-get install -y python make g++
|
||||||
# Run the install before copying the rest of the files
|
# Run the install before copying the rest of the files
|
||||||
RUN yarn config set workspaces-experimental true
|
RUN yarn config set workspaces-experimental true
|
||||||
RUN yarn install
|
RUN yarn install
|
||||||
@@ -52,7 +53,7 @@ RUN yarn run build
|
|||||||
|
|
||||||
# Stage 2: Bundle the built application into a Docker container
|
# Stage 2: Bundle the built application into a Docker container
|
||||||
# which runs Nginx using Alpine Linux
|
# which runs Nginx using Alpine Linux
|
||||||
FROM nginx:1.15.5-alpine
|
FROM nginx:1.21.1-alpine
|
||||||
RUN apk add --no-cache bash
|
RUN apk add --no-cache bash
|
||||||
RUN rm -rf /etc/nginx/conf.d
|
RUN rm -rf /etc/nginx/conf.d
|
||||||
COPY .docker/Viewer-v2.x /etc/nginx/conf.d
|
COPY .docker/Viewer-v2.x /etc/nginx/conf.d
|
||||||
|
|||||||
@@ -248,9 +248,6 @@ To acknowledge the OHIF Viewer in an academic publication, please cite
|
|||||||
>
|
>
|
||||||
> _Cancer Research_, November 1 2017 (77) (21) e119-e122 DOI:
|
> _Cancer Research_, November 1 2017 (77) (21) e119-e122 DOI:
|
||||||
> [10.1158/0008-5472.CAN-17-0334](https://www.doi.org/10.1158/0008-5472.CAN-17-0334)
|
> [10.1158/0008-5472.CAN-17-0334](https://www.doi.org/10.1158/0008-5472.CAN-17-0334)
|
||||||
>
|
|
||||||
> NCI Imaging Data Commons: contract number 19X037Q from Leidos Biomedical
|
|
||||||
> Research under Task Order HHSN26100071 from NCI.
|
|
||||||
|
|
||||||
**Note:** If you use or find this repository helpful, please take the time to
|
**Note:** If you use or find this repository helpful, please take the time to
|
||||||
star this repository on Github. This is an easy way for us to assess adoption
|
star this repository on Github. This is an easy way for us to assess adoption
|
||||||
@@ -261,6 +258,22 @@ Cancer Institute, Informatics Technology for Cancer Research (ITCR) program,
|
|||||||
under a
|
under a
|
||||||
[grant to Dr. Gordon Harris at Massachusetts General Hospital (U24 CA199460)](https://projectreporter.nih.gov/project_info_description.cfm?aid=8971104).
|
[grant to Dr. Gordon Harris at Massachusetts General Hospital (U24 CA199460)](https://projectreporter.nih.gov/project_info_description.cfm?aid=8971104).
|
||||||
|
|
||||||
|
## Projects that contributed to OHIF Viewer
|
||||||
|
|
||||||
|
The following is a (partial) list of projects that contributed resources towards
|
||||||
|
development of OHIF Viewer:
|
||||||
|
|
||||||
|
- [NCI Imaging Data Commons (IDC) project](https://imaging.datacommons.cancer.gov/)
|
||||||
|
supported development of new features and bug fixes marked with
|
||||||
|
["IDC:priority"](https://github.com/OHIF/Viewers/issues?q=is%3Aissue+is%3Aopen+label%3AIDC%3Apriority),
|
||||||
|
["IDC:candidate"](https://github.com/OHIF/Viewers/issues?q=is%3Aissue+is%3Aopen+label%3AIDC%3Acandidate)
|
||||||
|
or
|
||||||
|
["IDC:collaboration"](https://github.com/OHIF/Viewers/issues?q=is%3Aissue+is%3Aopen+label%3AIDC%3Acollaboration).
|
||||||
|
NCI Imaging Data Commons is supported by the contract number 19X037Q from
|
||||||
|
Leidos Biomedical Research under Task Order HHSN26100071 from NCI.
|
||||||
|
[IDC Viewer](https://learn.canceridc.dev/portal/visualization) is a customized
|
||||||
|
version of the OHIF Viewer.
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
MIT © [OHIF](https://github.com/OHIF)
|
MIT © [OHIF](https://github.com/OHIF)
|
||||||
|
|||||||
@@ -8,7 +8,6 @@
|
|||||||
"-fontsettings",
|
"-fontsettings",
|
||||||
"github",
|
"github",
|
||||||
"ga",
|
"ga",
|
||||||
"sitemap",
|
|
||||||
"anchors",
|
"anchors",
|
||||||
"versions"
|
"versions"
|
||||||
],
|
],
|
||||||
@@ -24,7 +23,8 @@
|
|||||||
"token": "UA-110573590-2"
|
"token": "UA-110573590-2"
|
||||||
},
|
},
|
||||||
"sitemap": {
|
"sitemap": {
|
||||||
"hostname": "https://docs.ohif.org"
|
"hostname": "https://docs.ohif.org",
|
||||||
|
"todo": "sitemap plugin removed; causing build errors"
|
||||||
},
|
},
|
||||||
"versions": {
|
"versions": {
|
||||||
"gitbookConfigURL": "https://raw.githubusercontent.com/OHIF/Viewers/master/docs/book.json",
|
"gitbookConfigURL": "https://raw.githubusercontent.com/OHIF/Viewers/master/docs/book.json",
|
||||||
|
|||||||
+2
-2
@@ -8,7 +8,6 @@
|
|||||||
"-fontsettings",
|
"-fontsettings",
|
||||||
"github",
|
"github",
|
||||||
"ga",
|
"ga",
|
||||||
"sitemap",
|
|
||||||
"anchors",
|
"anchors",
|
||||||
"versions"
|
"versions"
|
||||||
],
|
],
|
||||||
@@ -24,7 +23,8 @@
|
|||||||
"token": "UA-110573590-2"
|
"token": "UA-110573590-2"
|
||||||
},
|
},
|
||||||
"sitemap": {
|
"sitemap": {
|
||||||
"hostname": "https://docs.ohif.org"
|
"hostname": "https://docs.ohif.org",
|
||||||
|
"todo": "sitemap plugin removed; causing build errors"
|
||||||
},
|
},
|
||||||
"versions": {
|
"versions": {
|
||||||
"gitbookConfigURL": "https://raw.githubusercontent.com/OHIF/Viewers/master/docs/book.json",
|
"gitbookConfigURL": "https://raw.githubusercontent.com/OHIF/Viewers/master/docs/book.json",
|
||||||
|
|||||||
@@ -3,6 +3,92 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
## [2.10.10](https://github.com/OHIF/Viewers/compare/@ohif/extension-cornerstone@2.10.9...@ohif/extension-cornerstone@2.10.10) (2021-07-26)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* changed new image handler conditional for 0 edge case ([#2338](https://github.com/OHIF/Viewers/issues/2338)) ([b8ae92c](https://github.com/OHIF/Viewers/commit/b8ae92c2d4f733b3ca89826118029685650b396a))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [2.10.9](https://github.com/OHIF/Viewers/compare/@ohif/extension-cornerstone@2.10.8...@ohif/extension-cornerstone@2.10.9) (2021-06-02)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **dicom-html:** Add parsed dicom meta info section ([#2419](https://github.com/OHIF/Viewers/issues/2419)) ([403688b](https://github.com/OHIF/Viewers/commit/403688b18c52468fc1101166ec0c1734fb710039))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [2.10.8](https://github.com/OHIF/Viewers/compare/@ohif/extension-cornerstone@2.10.7...@ohif/extension-cornerstone@2.10.8) (2021-05-17)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-cornerstone
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [2.10.7](https://github.com/OHIF/Viewers/compare/@ohif/extension-cornerstone@2.10.6...@ohif/extension-cornerstone@2.10.7) (2021-04-15)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-cornerstone
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [2.10.6](https://github.com/OHIF/Viewers/compare/@ohif/extension-cornerstone@2.10.5...@ohif/extension-cornerstone@2.10.6) (2021-03-26)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-cornerstone
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [2.10.5](https://github.com/OHIF/Viewers/compare/@ohif/extension-cornerstone@2.10.4...@ohif/extension-cornerstone@2.10.5) (2021-03-15)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-cornerstone
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [2.10.4](https://github.com/OHIF/Viewers/compare/@ohif/extension-cornerstone@2.10.3...@ohif/extension-cornerstone@2.10.4) (2021-03-09)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-cornerstone
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [2.10.3](https://github.com/OHIF/Viewers/compare/@ohif/extension-cornerstone@2.10.2...@ohif/extension-cornerstone@2.10.3) (2021-03-03)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-cornerstone
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [2.10.2](https://github.com/OHIF/Viewers/compare/@ohif/extension-cornerstone@2.10.1...@ohif/extension-cornerstone@2.10.2) (2021-02-25)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-cornerstone
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [2.10.1](https://github.com/OHIF/Viewers/compare/@ohif/extension-cornerstone@2.10.0...@ohif/extension-cornerstone@2.10.1) (2021-02-05)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-cornerstone
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# [2.10.0](https://github.com/OHIF/Viewers/compare/@ohif/extension-cornerstone@2.9.6...@ohif/extension-cornerstone@2.10.0) (2020-12-10)
|
# [2.10.0](https://github.com/OHIF/Viewers/compare/@ohif/extension-cornerstone@2.9.6...@ohif/extension-cornerstone@2.10.0) (2020-12-10)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@ohif/extension-cornerstone",
|
"name": "@ohif/extension-cornerstone",
|
||||||
"version": "2.10.0",
|
"version": "2.10.10",
|
||||||
"description": "OHIF extension for Cornerstone",
|
"description": "OHIF extension for Cornerstone",
|
||||||
"author": "OHIF",
|
"author": "OHIF",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
@@ -36,7 +36,7 @@
|
|||||||
"cornerstone-math": "^0.1.8",
|
"cornerstone-math": "^0.1.8",
|
||||||
"cornerstone-tools": "^4.20.1",
|
"cornerstone-tools": "^4.20.1",
|
||||||
"cornerstone-wado-image-loader": "^3.1.0",
|
"cornerstone-wado-image-loader": "^3.1.0",
|
||||||
"dcmjs": "0.17.0",
|
"dcmjs": "0.18.8",
|
||||||
"dicom-parser": "^1.8.3",
|
"dicom-parser": "^1.8.3",
|
||||||
"hammerjs": "^2.0.8",
|
"hammerjs": "^2.0.8",
|
||||||
"prop-types": "^15.6.2",
|
"prop-types": "^15.6.2",
|
||||||
@@ -51,6 +51,6 @@
|
|||||||
"classnames": "^2.2.6",
|
"classnames": "^2.2.6",
|
||||||
"lodash.merge": "^4.6.2",
|
"lodash.merge": "^4.6.2",
|
||||||
"lodash.throttle": "^4.1.1",
|
"lodash.throttle": "^4.1.1",
|
||||||
"react-cornerstone-viewport": "4.0.4"
|
"react-cornerstone-viewport": "4.0.5"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
|
|
||||||
|
import OHIFCornerstoneViewportOverlay from './components/OHIFCornerstoneViewportOverlay'
|
||||||
import ConnectedCornerstoneViewport from './ConnectedCornerstoneViewport';
|
import ConnectedCornerstoneViewport from './ConnectedCornerstoneViewport';
|
||||||
import OHIF from '@ohif/core';
|
import OHIF from '@ohif/core';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
@@ -194,6 +195,7 @@ class OHIFCornerstoneViewport extends Component {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
const { viewportIndex } = this.props;
|
const { viewportIndex } = this.props;
|
||||||
|
const { inconsistencyWarnings } = this.props.viewportData.displaySet;
|
||||||
const {
|
const {
|
||||||
imageIds,
|
imageIds,
|
||||||
currentImageIdIndex,
|
currentImageIdIndex,
|
||||||
@@ -219,7 +221,7 @@ class OHIFCornerstoneViewport extends Component {
|
|||||||
const { displaySet } = this.props.viewportData;
|
const { displaySet } = this.props.viewportData;
|
||||||
const { StudyInstanceUID } = displaySet;
|
const { StudyInstanceUID } = displaySet;
|
||||||
|
|
||||||
if (currentImageIdIndex > 0) {
|
if (currentImageIdIndex >= 0) {
|
||||||
this.props.onNewImage({
|
this.props.onNewImage({
|
||||||
StudyInstanceUID,
|
StudyInstanceUID,
|
||||||
SOPInstanceUID: sopInstanceUid,
|
SOPInstanceUID: sopInstanceUid,
|
||||||
@@ -229,14 +231,19 @@ class OHIFCornerstoneViewport extends Component {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const warningsOverlay = props => {
|
||||||
|
return <OHIFCornerstoneViewportOverlay {...props} inconsistencyWarnings={inconsistencyWarnings} />
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<ConnectedCornerstoneViewport
|
<ConnectedCornerstoneViewport
|
||||||
viewportIndex={viewportIndex}
|
viewportIndex={viewportIndex}
|
||||||
imageIds={imageIds}
|
imageIds={imageIds}
|
||||||
imageIdIndex={currentImageIdIndex}
|
imageIdIndex={currentImageIdIndex}
|
||||||
onNewImage={newImageHandler}
|
onNewImageDebounced={newImageHandler}
|
||||||
onNewImageDebounceTime={700}
|
onNewImageDebounceTime={300}
|
||||||
|
viewportOverlayComponent={warningsOverlay}
|
||||||
// ~~ Connected (From REDUX)
|
// ~~ Connected (From REDUX)
|
||||||
// frameRate={frameRate}
|
// frameRate={frameRate}
|
||||||
// isPlaying={false}
|
// isPlaying={false}
|
||||||
|
|||||||
@@ -268,6 +268,7 @@ const commandsModule = ({ servicesManager }) => {
|
|||||||
SOPInstanceUID,
|
SOPInstanceUID,
|
||||||
frameIndex,
|
frameIndex,
|
||||||
activeViewportIndex,
|
activeViewportIndex,
|
||||||
|
refreshViewports = true,
|
||||||
}) => {
|
}) => {
|
||||||
const study = studyMetadataManager.get(StudyInstanceUID);
|
const study = studyMetadataManager.get(StudyInstanceUID);
|
||||||
|
|
||||||
@@ -278,6 +279,10 @@ const commandsModule = ({ servicesManager }) => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (!displaySet) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
displaySet.SOPInstanceUID = SOPInstanceUID;
|
displaySet.SOPInstanceUID = SOPInstanceUID;
|
||||||
displaySet.frameIndex = frameIndex;
|
displaySet.frameIndex = frameIndex;
|
||||||
|
|
||||||
@@ -285,7 +290,9 @@ const commandsModule = ({ servicesManager }) => {
|
|||||||
setViewportSpecificData(activeViewportIndex, displaySet)
|
setViewportSpecificData(activeViewportIndex, displaySet)
|
||||||
);
|
);
|
||||||
|
|
||||||
refreshCornerstoneViewports();
|
if (refreshViewports) {
|
||||||
|
refreshCornerstoneViewports();
|
||||||
|
}
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,69 @@
|
|||||||
|
.imageViewerViewport.empty ~ .OHIFCornerstoneViewportOverlay {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.OHIFCornerstoneViewportOverlay {
|
||||||
|
color: #9ccef9;
|
||||||
|
}
|
||||||
|
.OHIFCornerstoneViewportOverlay .overlay-element {
|
||||||
|
position: absolute;
|
||||||
|
font-weight: 400;
|
||||||
|
text-shadow: 1px 1px #000;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
.OHIFCornerstoneViewportOverlay .top-left {
|
||||||
|
top: 20px;
|
||||||
|
left: 20px;
|
||||||
|
}
|
||||||
|
.OHIFCornerstoneViewportOverlay .top-center {
|
||||||
|
top: 20px;
|
||||||
|
padding-top: 20px;
|
||||||
|
width: 100%;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.OHIFCornerstoneViewportOverlay .top-right {
|
||||||
|
top: 20px;
|
||||||
|
right: 20px;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
.OHIFCornerstoneViewportOverlay .bottom-left {
|
||||||
|
bottom: 20px;
|
||||||
|
left: 20px;
|
||||||
|
}
|
||||||
|
.OHIFCornerstoneViewportOverlay .bottom-left2 {
|
||||||
|
bottom: 140px;
|
||||||
|
left: 20px;
|
||||||
|
}
|
||||||
|
.OHIFCornerstoneViewportOverlay .bottom-right {
|
||||||
|
bottom: 20px;
|
||||||
|
right: 20px;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
.OHIFCornerstoneViewportOverlay.controlsVisible .topright,
|
||||||
|
.OHIFCornerstoneViewportOverlay.controlsVisible .bottomright {
|
||||||
|
right: calc(20px + 19px);
|
||||||
|
}
|
||||||
|
.OHIFCornerstoneViewportOverlay .warning {
|
||||||
|
position: absolute;
|
||||||
|
font-weight: 400;
|
||||||
|
text-shadow: 1px 1px #000;
|
||||||
|
pointer-events: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
.OHIFCornerstoneViewportOverlay .warning svg {
|
||||||
|
opacity: 1;
|
||||||
|
color: #e29e4a;
|
||||||
|
fill: #e29e4a;
|
||||||
|
stroke: #e29e4a;
|
||||||
|
width: 32px;
|
||||||
|
height: 28px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.OHIFCornerstoneViewportOverlay svg {
|
||||||
|
color: #9ccef9;
|
||||||
|
fill: #9ccef9;
|
||||||
|
stroke: #9ccef9;
|
||||||
|
background-color: transparent;
|
||||||
|
margin: 2px;
|
||||||
|
width: 18px;
|
||||||
|
height: 18px;
|
||||||
|
}
|
||||||
@@ -0,0 +1,167 @@
|
|||||||
|
import { PureComponent } from 'react';
|
||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import cornerstone from 'cornerstone-core';
|
||||||
|
import './OHIFCornerstoneViewportOverlay.css';
|
||||||
|
import {
|
||||||
|
isValidNumber,
|
||||||
|
formatNumberPrecision,
|
||||||
|
formatDICOMDate,
|
||||||
|
formatDICOMTime,
|
||||||
|
formatPN,
|
||||||
|
getCompression
|
||||||
|
} from '../utils/formatStudy';
|
||||||
|
import classNames from 'classnames';
|
||||||
|
import { Icon } from '@ohif/ui/src/elements/Icon';
|
||||||
|
import { Tooltip } from '@ohif/ui/src/components/tooltip';
|
||||||
|
import { OverlayTrigger } from '@ohif/ui/src/components/overlayTrigger';
|
||||||
|
|
||||||
|
class OHIFCornerstoneViewportOverlay extends PureComponent {
|
||||||
|
static propTypes = {
|
||||||
|
scale: PropTypes.number.isRequired,
|
||||||
|
windowWidth: PropTypes.oneOfType([
|
||||||
|
PropTypes.number.isRequired,
|
||||||
|
PropTypes.string.isRequired,
|
||||||
|
]),
|
||||||
|
windowCenter: PropTypes.oneOfType([
|
||||||
|
PropTypes.number.isRequired,
|
||||||
|
PropTypes.string.isRequired,
|
||||||
|
]),
|
||||||
|
imageId: PropTypes.string.isRequired,
|
||||||
|
imageIndex: PropTypes.number.isRequired,
|
||||||
|
stackSize: PropTypes.number.isRequired,
|
||||||
|
inconsistencyWarnings: PropTypes.array.isRequired
|
||||||
|
};
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { imageId, scale, windowWidth, windowCenter, inconsistencyWarnings } = this.props;
|
||||||
|
|
||||||
|
if (!imageId) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const zoomPercentage = formatNumberPrecision(scale * 100, 0);
|
||||||
|
const seriesMetadata =
|
||||||
|
cornerstone.metaData.get('generalSeriesModule', imageId) || {};
|
||||||
|
const imagePlaneModule =
|
||||||
|
cornerstone.metaData.get('imagePlaneModule', imageId) || {};
|
||||||
|
const { rows, columns, sliceThickness, sliceLocation } = imagePlaneModule;
|
||||||
|
const { seriesNumber, seriesDescription } = seriesMetadata;
|
||||||
|
|
||||||
|
const generalStudyModule =
|
||||||
|
cornerstone.metaData.get('generalStudyModule', imageId) || {};
|
||||||
|
const { studyDate, studyTime, studyDescription } = generalStudyModule;
|
||||||
|
|
||||||
|
const patientModule =
|
||||||
|
cornerstone.metaData.get('patientModule', imageId) || {};
|
||||||
|
const { patientId, patientName } = patientModule;
|
||||||
|
|
||||||
|
const generalImageModule =
|
||||||
|
cornerstone.metaData.get('generalImageModule', imageId) || {};
|
||||||
|
const { instanceNumber } = generalImageModule;
|
||||||
|
|
||||||
|
const cineModule = cornerstone.metaData.get('cineModule', imageId) || {};
|
||||||
|
const { frameTime } = cineModule;
|
||||||
|
|
||||||
|
const frameRate = formatNumberPrecision(1000 / frameTime, 1);
|
||||||
|
const compression = getCompression(imageId);
|
||||||
|
const wwwc = `W: ${
|
||||||
|
windowWidth.toFixed ? windowWidth.toFixed(0) : windowWidth
|
||||||
|
} L: ${windowWidth.toFixed ? windowCenter.toFixed(0) : windowCenter}`;
|
||||||
|
const imageDimensions = `${columns} x ${rows}`;
|
||||||
|
|
||||||
|
const { imageIndex, stackSize } = this.props;
|
||||||
|
|
||||||
|
const inconsistencyWarningsOn = inconsistencyWarnings && inconsistencyWarnings.length !== 0 ? true : false;
|
||||||
|
const getWarningContent = (warningList) => {
|
||||||
|
if (Array.isArray(warningList)) {
|
||||||
|
const listedWarnings = warningList.map((warn, index) => {
|
||||||
|
return <li key={index}>{warn}</li>;
|
||||||
|
});
|
||||||
|
|
||||||
|
return <ol>{listedWarnings}</ol>;
|
||||||
|
} else {
|
||||||
|
return <React.Fragment>{warningList}</React.Fragment>;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const getWarningInfo = (seriesNumber, inconsistencyWarnings) => {
|
||||||
|
return(
|
||||||
|
<React.Fragment>
|
||||||
|
{inconsistencyWarnings.length != 0 ? (
|
||||||
|
<OverlayTrigger
|
||||||
|
key={seriesNumber}
|
||||||
|
placement="left"
|
||||||
|
overlay={
|
||||||
|
<Tooltip
|
||||||
|
placement="left"
|
||||||
|
className="in tooltip-warning"
|
||||||
|
id="tooltip-left"
|
||||||
|
>
|
||||||
|
<div className="warningTitle">Series Inconsistencies</div>
|
||||||
|
<div className="warningContent">{getWarningContent(inconsistencyWarnings)}</div>
|
||||||
|
</Tooltip>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<div className={classNames('warning')}>
|
||||||
|
<span className="warning-icon">
|
||||||
|
<Icon name="exclamation-triangle" />
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</OverlayTrigger>
|
||||||
|
) : (
|
||||||
|
<React.Fragment></React.Fragment>
|
||||||
|
)}
|
||||||
|
</React.Fragment>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const normal = (
|
||||||
|
<React.Fragment>
|
||||||
|
<div className="top-left overlay-element">
|
||||||
|
<div>{formatPN(patientName)}</div>
|
||||||
|
<div>{patientId}</div>
|
||||||
|
</div>
|
||||||
|
<div className="top-right overlay-element">
|
||||||
|
<div>{studyDescription}</div>
|
||||||
|
<div>
|
||||||
|
{formatDICOMDate(studyDate)} {formatDICOMTime(studyTime)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="bottom-right overlay-element">
|
||||||
|
<div>Zoom: {zoomPercentage}%</div>
|
||||||
|
<div>{wwwc}</div>
|
||||||
|
<div className="compressionIndicator">{compression}</div>
|
||||||
|
</div>
|
||||||
|
<div className="bottom-left2 warning">
|
||||||
|
<div>{inconsistencyWarningsOn ? getWarningInfo(seriesNumber, inconsistencyWarnings) : ''}</div>
|
||||||
|
</div>
|
||||||
|
<div className="bottom-left overlay-element">
|
||||||
|
<div>{seriesNumber >= 0 ? `Ser: ${seriesNumber}` : ''}</div>
|
||||||
|
<div>
|
||||||
|
{stackSize > 1
|
||||||
|
? `Img: ${instanceNumber} ${imageIndex}/${stackSize}`
|
||||||
|
: ''}
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
{frameRate >= 0 ? `${formatNumberPrecision(frameRate, 2)} FPS` : ''}
|
||||||
|
<div>{imageDimensions}</div>
|
||||||
|
<div>
|
||||||
|
{isValidNumber(sliceLocation)
|
||||||
|
? `Loc: ${formatNumberPrecision(sliceLocation, 2)} mm `
|
||||||
|
: ''}
|
||||||
|
{sliceThickness
|
||||||
|
? `Thick: ${formatNumberPrecision(sliceThickness, 2)} mm`
|
||||||
|
: ''}
|
||||||
|
</div>
|
||||||
|
<div>{seriesDescription}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</React.Fragment>
|
||||||
|
);
|
||||||
|
|
||||||
|
return <div className="OHIFCornerstoneViewportOverlay">{normal}</div>;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default OHIFCornerstoneViewportOverlay;
|
||||||
@@ -38,7 +38,14 @@ export default {
|
|||||||
},
|
},
|
||||||
getViewportModule({ commandsManager }) {
|
getViewportModule({ commandsManager }) {
|
||||||
const ExtendedOHIFCornerstoneViewport = props => {
|
const ExtendedOHIFCornerstoneViewport = props => {
|
||||||
|
/**
|
||||||
|
* TODO: This appears to be used to set the redux parameters for
|
||||||
|
* the viewport when new images are loaded. It's very ugly
|
||||||
|
* and we should remove it.
|
||||||
|
*/
|
||||||
const onNewImageHandler = jumpData => {
|
const onNewImageHandler = jumpData => {
|
||||||
|
/** Do not trigger all viewports to render unnecessarily */
|
||||||
|
jumpData.refreshViewports = false;
|
||||||
commandsManager.runCommand('jumpToImage', jumpData);
|
commandsManager.runCommand('jumpToImage', jumpData);
|
||||||
};
|
};
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -0,0 +1,109 @@
|
|||||||
|
import moment from 'moment';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if value is valid.
|
||||||
|
*
|
||||||
|
* @param {number} value
|
||||||
|
* @returns {boolean} is valid.
|
||||||
|
*/
|
||||||
|
function isValidNumber(value) {
|
||||||
|
return typeof value === 'number' && !isNaN(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Formats number precision.
|
||||||
|
*
|
||||||
|
* @param {number} number
|
||||||
|
* @param {number} precision
|
||||||
|
* @returns {number} formatted number.
|
||||||
|
*/
|
||||||
|
function formatNumberPrecision(number, precision) {
|
||||||
|
if (number !== null) {
|
||||||
|
return parseFloat(number).toFixed(precision);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Formats DICOM date.
|
||||||
|
*
|
||||||
|
* @param {string} date
|
||||||
|
* @param {string} strFormat
|
||||||
|
* @returns {string} formatted date.
|
||||||
|
*/
|
||||||
|
function formatDICOMDate(date, strFormat = 'MMM D, YYYY') {
|
||||||
|
return moment(date, 'YYYYMMDD').format(strFormat);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DICOM Time is stored as HHmmss.SSS, where:
|
||||||
|
* HH 24 hour time:
|
||||||
|
* m mm 0..59 Minutes
|
||||||
|
* s ss 0..59 Seconds
|
||||||
|
* S SS SSS 0..999 Fractional seconds
|
||||||
|
*
|
||||||
|
* Goal: '24:12:12'
|
||||||
|
*
|
||||||
|
* @param {*} time
|
||||||
|
* @param {string} strFormat
|
||||||
|
* @returns {string} formatted name.
|
||||||
|
*/
|
||||||
|
function formatDICOMTime(time, strFormat = 'HH:mm:ss') {
|
||||||
|
return moment(time, 'HH:mm:ss').format(strFormat);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Formats a patient name for display purposes
|
||||||
|
*
|
||||||
|
* @param {string} name
|
||||||
|
* @returns {string} formatted name.
|
||||||
|
*/
|
||||||
|
function formatPN(name) {
|
||||||
|
if (!name) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert the first ^ to a ', '. String.replace() only affects
|
||||||
|
// the first appearance of the character.
|
||||||
|
const commaBetweenFirstAndLast = name.replace('^', ', ');
|
||||||
|
|
||||||
|
// Replace any remaining '^' characters with spaces
|
||||||
|
const cleaned = commaBetweenFirstAndLast.replace(/\^/g, ' ');
|
||||||
|
|
||||||
|
// Trim any extraneous whitespace
|
||||||
|
return cleaned.trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets compression type
|
||||||
|
*
|
||||||
|
* @param {number} imageId
|
||||||
|
* @returns {string} comrpession type.
|
||||||
|
*/
|
||||||
|
function getCompression(imageId) {
|
||||||
|
const generalImageModule =
|
||||||
|
cornerstone.metaData.get('generalImageModule', imageId) || {};
|
||||||
|
const {
|
||||||
|
lossyImageCompression,
|
||||||
|
lossyImageCompressionRatio,
|
||||||
|
lossyImageCompressionMethod,
|
||||||
|
} = generalImageModule;
|
||||||
|
|
||||||
|
if (lossyImageCompression === '01' && lossyImageCompressionRatio !== '') {
|
||||||
|
const compressionMethod = lossyImageCompressionMethod || 'Lossy: ';
|
||||||
|
const compressionRatio = formatNumberPrecision(
|
||||||
|
lossyImageCompressionRatio,
|
||||||
|
2
|
||||||
|
);
|
||||||
|
return compressionMethod + compressionRatio + ' : 1';
|
||||||
|
}
|
||||||
|
|
||||||
|
return 'Lossless / Uncompressed';
|
||||||
|
}
|
||||||
|
|
||||||
|
export { isValidNumber,
|
||||||
|
formatNumberPrecision,
|
||||||
|
formatDICOMDate,
|
||||||
|
formatDICOMTime,
|
||||||
|
formatPN,
|
||||||
|
getCompression
|
||||||
|
};
|
||||||
@@ -3,6 +3,33 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
## [0.2.2](https://github.com/OHIF/Viewers/compare/@ohif/extension-debugging@0.2.1...@ohif/extension-debugging@0.2.2) (2021-06-03)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-debugging
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [0.2.1](https://github.com/OHIF/Viewers/compare/@ohif/extension-debugging@0.2.0...@ohif/extension-debugging@0.2.1) (2021-03-19)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-debugging
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# [0.2.0](https://github.com/OHIF/Viewers/compare/@ohif/extension-debugging@0.1.6...@ohif/extension-debugging@0.2.0) (2021-03-04)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **log:** add new log service ([14d6454](https://github.com/OHIF/Viewers/commit/14d6454eafaa2ccb50e133c2945c9558052ea27e))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## [0.1.6](https://github.com/OHIF/Viewers/compare/@ohif/extension-debugging@0.1.5...@ohif/extension-debugging@0.1.6) (2020-10-06)
|
## [0.1.6](https://github.com/OHIF/Viewers/compare/@ohif/extension-debugging@0.1.5...@ohif/extension-debugging@0.1.6) (2020-10-06)
|
||||||
|
|
||||||
**Note:** Version bump only for package @ohif/extension-debugging
|
**Note:** Version bump only for package @ohif/extension-debugging
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@ohif/extension-debugging",
|
"name": "@ohif/extension-debugging",
|
||||||
"version": "0.1.6",
|
"version": "0.2.2",
|
||||||
"description": "OHIF extension for debugging.",
|
"description": "OHIF extension for debugging.",
|
||||||
"author": "OHIF",
|
"author": "OHIF",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
@@ -28,13 +28,12 @@
|
|||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@ohif/core": "^2.6.0",
|
"@ohif/core": "^2.6.0",
|
||||||
"dicom-parser": "^1.8.3",
|
"dicom-parser": "^1.8.3"
|
||||||
"dicomweb-client": "^0.5.2"
|
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "^7.5.5",
|
"@babel/runtime": "^7.5.5",
|
||||||
"detect-browser": "5.1.1",
|
"detect-browser": "5.1.1",
|
||||||
"dicomweb-client": "^0.6.0",
|
"dicomweb-client": "^0.8.1",
|
||||||
"file-saver": "^2.0.2",
|
"file-saver": "^2.0.2",
|
||||||
"jszip": "^3.2.2"
|
"jszip": "^3.2.2"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,3 +12,15 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.debug-report-modal-container .errors {
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.debug-report-modal-container .errors-container {
|
||||||
|
border: 1px solid var(--active-color);
|
||||||
|
border-radius: 5px;
|
||||||
|
padding: 10px;
|
||||||
|
overflow: scroll;
|
||||||
|
max-height: 300px;
|
||||||
|
}
|
||||||
@@ -10,6 +10,7 @@ const DubugReportModal = ({
|
|||||||
extensionManager,
|
extensionManager,
|
||||||
mailTo,
|
mailTo,
|
||||||
debugModalMessage,
|
debugModalMessage,
|
||||||
|
errors = [],
|
||||||
}) => {
|
}) => {
|
||||||
const copyDebugDataToClipboard = () => {
|
const copyDebugDataToClipboard = () => {
|
||||||
const body = getEmailBody();
|
const body = getEmailBody();
|
||||||
@@ -131,6 +132,19 @@ const DubugReportModal = ({
|
|||||||
{getLayout(viewports)}
|
{getLayout(viewports)}
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="errors">
|
||||||
|
<h3>Errors ({errors.length})</h3>
|
||||||
|
<div className="errors-container">
|
||||||
|
{errors.map(error => {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<pre>Message: {error.message}</pre>
|
||||||
|
{error.error && <pre>Stack: {error.error.stack}</pre>}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
import OHIF from '@ohif/core';
|
import OHIF from '@ohif/core';
|
||||||
|
import { useLogger } from '@ohif/ui';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
save,
|
save,
|
||||||
upload,
|
upload,
|
||||||
@@ -95,6 +97,7 @@ export function getCommands(context, servicesManager, extensionManager) {
|
|||||||
const { UIModalService } = servicesManager.services;
|
const { UIModalService } = servicesManager.services;
|
||||||
|
|
||||||
const WrappedDebugReportModal = function() {
|
const WrappedDebugReportModal = function() {
|
||||||
|
const { state: loggerState } = useLogger();
|
||||||
return (
|
return (
|
||||||
<DebugReportModal
|
<DebugReportModal
|
||||||
viewports={viewports}
|
viewports={viewports}
|
||||||
@@ -103,6 +106,7 @@ export function getCommands(context, servicesManager, extensionManager) {
|
|||||||
extensionManager={extensionManager}
|
extensionManager={extensionManager}
|
||||||
mailTo={state.mailTo}
|
mailTo={state.mailTo}
|
||||||
debugModalMessage={state.debugModalMessage}
|
debugModalMessage={state.debugModalMessage}
|
||||||
|
errors={loggerState.errors}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
const state = { mailTo: undefined, message: undefined };
|
const state = { mailTo: undefined, debugModalMessage: undefined };
|
||||||
|
|
||||||
export default state;
|
export default state;
|
||||||
@@ -3,6 +3,89 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
## [1.3.10](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-html@1.3.9...@ohif/extension-dicom-html@1.3.10) (2021-06-02)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **dicom-html:** Add parsed dicom meta info section ([#2419](https://github.com/OHIF/Viewers/issues/2419)) ([403688b](https://github.com/OHIF/Viewers/commit/403688b18c52468fc1101166ec0c1734fb710039))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [1.3.9](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-html@1.3.8...@ohif/extension-dicom-html@1.3.9) (2021-04-22)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-dicom-html
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [1.3.8](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-html@1.3.7...@ohif/extension-dicom-html@1.3.8) (2021-04-22)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-dicom-html
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [1.3.7](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-html@1.3.6...@ohif/extension-dicom-html@1.3.7) (2021-04-21)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-dicom-html
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [1.3.6](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-html@1.3.5...@ohif/extension-dicom-html@1.3.6) (2021-04-21)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-dicom-html
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [1.3.5](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-html@1.3.4...@ohif/extension-dicom-html@1.3.5) (2021-04-15)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-dicom-html
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [1.3.4](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-html@1.3.3...@ohif/extension-dicom-html@1.3.4) (2021-03-09)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-dicom-html
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [1.3.3](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-html@1.3.2...@ohif/extension-dicom-html@1.3.3) (2021-03-03)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-dicom-html
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [1.3.2](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-html@1.3.1...@ohif/extension-dicom-html@1.3.2) (2021-02-25)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-dicom-html
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [1.3.1](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-html@1.3.0...@ohif/extension-dicom-html@1.3.1) (2021-02-05)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-dicom-html
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# [1.3.0](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-html@1.2.9...@ohif/extension-dicom-html@1.3.0) (2020-12-10)
|
# [1.3.0](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-html@1.2.9...@ohif/extension-dicom-html@1.3.0) (2020-12-10)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@ohif/extension-dicom-html",
|
"name": "@ohif/extension-dicom-html",
|
||||||
"version": "1.3.0",
|
"version": "1.3.10",
|
||||||
"description": "OHIF extension for rendering structured reports to HTML",
|
"description": "OHIF extension for rendering structured reports to HTML",
|
||||||
"author": "OHIF",
|
"author": "OHIF",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
@@ -29,7 +29,7 @@
|
|||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@ohif/core": "^0.50.0",
|
"@ohif/core": "^0.50.0",
|
||||||
"dcmjs": "0.17.0",
|
"dcmjs": "0.18.8",
|
||||||
"prop-types": "^15.6.2",
|
"prop-types": "^15.6.2",
|
||||||
"react": "^16.8.6",
|
"react": "^16.8.6",
|
||||||
"react-dom": "^16.8.6"
|
"react-dom": "^16.8.6"
|
||||||
|
|||||||
@@ -168,6 +168,16 @@ const getContentSequence = (data, level = 1) => {
|
|||||||
Object.keys(data).forEach(key => {
|
Object.keys(data).forEach(key => {
|
||||||
const value = data[key];
|
const value = data[key];
|
||||||
|
|
||||||
|
if (key === '_meta') {
|
||||||
|
const HeaderDynamicLevel = `h3`;
|
||||||
|
root.push(<hr />);
|
||||||
|
root.push(
|
||||||
|
<HeaderDynamicLevel key="Metadata">
|
||||||
|
DICOM File Meta Information
|
||||||
|
</HeaderDynamicLevel>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
let content;
|
let content;
|
||||||
if (value instanceof Object) {
|
if (value instanceof Object) {
|
||||||
content = getContentSequence(value, level + 1);
|
content = getContentSequence(value, level + 1);
|
||||||
@@ -215,6 +225,7 @@ class DicomHtmlViewport extends Component {
|
|||||||
const dicomData = DicomMessage.readFile(arrayBuffer);
|
const dicomData = DicomMessage.readFile(arrayBuffer);
|
||||||
const dataset = DicomMetaDictionary.naturalizeDataset(dicomData.dict);
|
const dataset = DicomMetaDictionary.naturalizeDataset(dicomData.dict);
|
||||||
dataset._meta = DicomMetaDictionary.namifyDataset(dicomData.meta);
|
dataset._meta = DicomMetaDictionary.namifyDataset(dicomData.meta);
|
||||||
|
dataset._meta = DicomMetaDictionary.naturalizeDataset(dataset._meta);
|
||||||
|
|
||||||
const mainData = getMainData(dataset);
|
const mainData = getMainData(dataset);
|
||||||
const contentSequence = getContentSequence(dataset);
|
const contentSequence = getContentSequence(dataset);
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ const SOP_CLASS_UIDS = {
|
|||||||
MAMMOGRAPHY_CAD_SR: '1.2.840.10008.5.1.4.1.1.88.50',
|
MAMMOGRAPHY_CAD_SR: '1.2.840.10008.5.1.4.1.1.88.50',
|
||||||
CHEST_CAD_SR: '1.2.840.10008.5.1.4.1.1.88.65',
|
CHEST_CAD_SR: '1.2.840.10008.5.1.4.1.1.88.65',
|
||||||
X_RAY_RADIATION_DOSE_SR: '1.2.840.10008.5.1.4.1.1.88.67',
|
X_RAY_RADIATION_DOSE_SR: '1.2.840.10008.5.1.4.1.1.88.67',
|
||||||
|
ACQUISITION_CONTEXT_SR_STORAGE: '1.2.840.10008.5.1.4.1.1.88.71',
|
||||||
};
|
};
|
||||||
|
|
||||||
const sopClassUIDs = Object.values(SOP_CLASS_UIDS);
|
const sopClassUIDs = Object.values(SOP_CLASS_UIDS);
|
||||||
|
|||||||
@@ -3,6 +3,17 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
# [0.52.0](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-microscopy@0.51.4...@ohif/extension-dicom-microscopy@0.52.0) (2021-03-04)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **log:** add new log service ([14d6454](https://github.com/OHIF/Viewers/commit/14d6454eafaa2ccb50e133c2945c9558052ea27e))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## [0.51.4](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-microscopy@0.51.3...@ohif/extension-dicom-microscopy@0.51.4) (2021-01-21)
|
## [0.51.4](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-microscopy@0.51.3...@ohif/extension-dicom-microscopy@0.51.4) (2021-01-21)
|
||||||
|
|
||||||
**Note:** Version bump only for package @ohif/extension-dicom-microscopy
|
**Note:** Version bump only for package @ohif/extension-dicom-microscopy
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@ohif/extension-dicom-microscopy",
|
"name": "@ohif/extension-dicom-microscopy",
|
||||||
"version": "0.51.4",
|
"version": "0.52.0",
|
||||||
"description": "OHIF extension for Dicom Microscopy",
|
"description": "OHIF extension for Dicom Microscopy",
|
||||||
"author": "OHIF",
|
"author": "OHIF",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
|||||||
@@ -70,12 +70,18 @@ class DicomMicroscopyViewport extends Component {
|
|||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('[Microscopy Viewer] Failed to load:', error);
|
console.error('[Microscopy Viewer] Failed to load:', error);
|
||||||
const { UINotificationService } = this.props.servicesManager.services;
|
const {
|
||||||
|
UINotificationService,
|
||||||
|
LoggerService,
|
||||||
|
} = this.props.servicesManager.services;
|
||||||
if (UINotificationService) {
|
if (UINotificationService) {
|
||||||
|
const message =
|
||||||
|
'Failed to load viewport. Please check that you have hardware acceleration enabled.';
|
||||||
|
LoggerService.error({ error, message });
|
||||||
UINotificationService.show({
|
UINotificationService.show({
|
||||||
|
autoClose: false,
|
||||||
title: 'Microscopy Viewport',
|
title: 'Microscopy Viewport',
|
||||||
message:
|
message,
|
||||||
'Failed to load viewport. Please check that you have hardware acceleration enabled.',
|
|
||||||
type: 'error',
|
type: 'error',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,97 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
## [0.6.14](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-rt@0.6.13...@ohif/extension-dicom-rt@0.6.14) (2021-09-03)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-dicom-rt
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [0.6.13](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-rt@0.6.12...@ohif/extension-dicom-rt@0.6.13) (2021-06-02)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **dicom-html:** Add parsed dicom meta info section ([#2419](https://github.com/OHIF/Viewers/issues/2419)) ([403688b](https://github.com/OHIF/Viewers/commit/403688b18c52468fc1101166ec0c1734fb710039))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [0.6.12](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-rt@0.6.11...@ohif/extension-dicom-rt@0.6.12) (2021-05-17)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-dicom-rt
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [0.6.11](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-rt@0.6.10...@ohif/extension-dicom-rt@0.6.11) (2021-04-22)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-dicom-rt
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [0.6.10](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-rt@0.6.9...@ohif/extension-dicom-rt@0.6.10) (2021-04-22)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-dicom-rt
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [0.6.9](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-rt@0.6.8...@ohif/extension-dicom-rt@0.6.9) (2021-04-21)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-dicom-rt
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [0.6.8](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-rt@0.6.7...@ohif/extension-dicom-rt@0.6.8) (2021-04-15)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-dicom-rt
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [0.6.7](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-rt@0.6.6...@ohif/extension-dicom-rt@0.6.7) (2021-03-09)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-dicom-rt
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [0.6.6](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-rt@0.6.5...@ohif/extension-dicom-rt@0.6.6) (2021-03-03)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-dicom-rt
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [0.6.5](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-rt@0.6.4...@ohif/extension-dicom-rt@0.6.5) (2021-02-25)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-dicom-rt
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [0.6.4](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-rt@0.6.3...@ohif/extension-dicom-rt@0.6.4) (2021-02-05)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-dicom-rt
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## [0.6.3](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-rt@0.6.2...@ohif/extension-dicom-rt@0.6.3) (2021-01-21)
|
## [0.6.3](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-rt@0.6.2...@ohif/extension-dicom-rt@0.6.3) (2021-01-21)
|
||||||
|
|
||||||
**Note:** Version bump only for package @ohif/extension-dicom-rt
|
**Note:** Version bump only for package @ohif/extension-dicom-rt
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@ohif/extension-dicom-rt",
|
"name": "@ohif/extension-dicom-rt",
|
||||||
"version": "0.6.3",
|
"version": "0.6.14",
|
||||||
"description": "OHIF extension for rendering DICOM RTSTRUCTs on top of cornerstone images.",
|
"description": "OHIF extension for rendering DICOM RTSTRUCTs on top of cornerstone images.",
|
||||||
"author": "OHIF",
|
"author": "OHIF",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
@@ -31,7 +31,7 @@
|
|||||||
"@ohif/core": "^0.50.0",
|
"@ohif/core": "^0.50.0",
|
||||||
"cornerstone-core": "^2.2.8",
|
"cornerstone-core": "^2.2.8",
|
||||||
"cornerstone-tools": "^4.20.1",
|
"cornerstone-tools": "^4.20.1",
|
||||||
"dcmjs": "0.17.0",
|
"dcmjs": "0.18.8",
|
||||||
"gl-matrix": "^3.3.0",
|
"gl-matrix": "^3.3.0",
|
||||||
"prop-types": "^15.6.2",
|
"prop-types": "^15.6.2",
|
||||||
"react": "^16.8.6",
|
"react": "^16.8.6",
|
||||||
|
|||||||
@@ -71,8 +71,8 @@ const OHIFDicomRTStructSopClassHandler = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
rtStructDisplaySet.getSourceDisplaySet = function (studies) {
|
rtStructDisplaySet.getSourceDisplaySet = function (studies, activateLabelMap = true) {
|
||||||
return getSourceDisplaySet(studies, rtStructDisplaySet);
|
return getSourceDisplaySet(studies, rtStructDisplaySet, activateLabelMap);
|
||||||
};
|
};
|
||||||
|
|
||||||
rtStructDisplaySet.load = function (referencedDisplaySet, studies) {
|
rtStructDisplaySet.load = function (referencedDisplaySet, studies) {
|
||||||
|
|||||||
@@ -1,38 +1,14 @@
|
|||||||
export default function getSourceDisplaySet(studies, rtStructDisplaySet) {
|
import { metadata } from '@ohif/core';
|
||||||
const referencedDisplaySet = _getReferencedDisplaySet(
|
|
||||||
|
export default function getSourceDisplaySet(studies, rtStructDisplaySet, activateLabelMap = true) {
|
||||||
|
const referencedDisplaySet = metadata.StudyMetadata.getReferencedDisplaySet(
|
||||||
rtStructDisplaySet,
|
rtStructDisplaySet,
|
||||||
studies
|
studies
|
||||||
);
|
);
|
||||||
|
|
||||||
rtStructDisplaySet.load(referencedDisplaySet, studies);
|
if (activateLabelMap) {
|
||||||
|
rtStructDisplaySet.load(referencedDisplaySet, studies);
|
||||||
|
}
|
||||||
|
|
||||||
return referencedDisplaySet;
|
return referencedDisplaySet;
|
||||||
}
|
}
|
||||||
|
|
||||||
const _getReferencedDisplaySet = (rtStructDisplaySet, studies) => {
|
|
||||||
let allDisplaySets = [];
|
|
||||||
|
|
||||||
studies.forEach(study => {
|
|
||||||
allDisplaySets = allDisplaySets.concat(study.displaySets);
|
|
||||||
});
|
|
||||||
|
|
||||||
const otherDisplaySets = allDisplaySets.filter(
|
|
||||||
ds => ds.displaySetInstanceUID !== rtStructDisplaySet.displaySetInstanceUID
|
|
||||||
);
|
|
||||||
|
|
||||||
const ReferencedSeriesSequence = Array.isArray(
|
|
||||||
rtStructDisplaySet.metadata.ReferencedSeriesSequence
|
|
||||||
)
|
|
||||||
? rtStructDisplaySet.metadata.ReferencedSeriesSequence
|
|
||||||
: [rtStructDisplaySet.metadata.ReferencedSeriesSequence];
|
|
||||||
|
|
||||||
const referencedSeriesInstanceUIDs = ReferencedSeriesSequence.map(
|
|
||||||
ReferencedSeries => ReferencedSeries.SeriesInstanceUID
|
|
||||||
);
|
|
||||||
|
|
||||||
const referencedDisplaySet = otherDisplaySets.find(ds =>
|
|
||||||
referencedSeriesInstanceUIDs.includes(ds.SeriesInstanceUID)
|
|
||||||
);
|
|
||||||
|
|
||||||
return referencedDisplaySet;
|
|
||||||
};
|
|
||||||
@@ -1,11 +1,12 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import { utils } from '@ohif/core';
|
||||||
|
|
||||||
import init from './init.js';
|
import init from './init.js';
|
||||||
import sopClassHandlerModule from './OHIFDicomRTStructSopClassHandler';
|
import sopClassHandlerModule from './OHIFDicomRTStructSopClassHandler';
|
||||||
import id from './id.js';
|
import id from './id.js';
|
||||||
import RTPanel from './components/RTPanel/RTPanel';
|
import RTPanel from './components/RTPanel/RTPanel';
|
||||||
import { version } from '../package.json';
|
import { version } from '../package.json';
|
||||||
|
|
||||||
import { utils } from '@ohif/core';
|
|
||||||
const { studyMetadataManager } = utils;
|
const { studyMetadataManager } = utils;
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
@@ -42,12 +43,48 @@ export default {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const RTPanelTabChangedEvent = 'rt-panel-tab-updated';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Trigger's an event to update the state of the panel's RoundedButtonGroup.
|
||||||
|
*
|
||||||
|
* This is required to avoid extension state
|
||||||
|
* coupling with the viewer's ToolbarRow component.
|
||||||
|
*
|
||||||
|
* @param {object} data
|
||||||
|
*/
|
||||||
|
const triggerRTPanelUpdatedEvent = data => {
|
||||||
|
const event = new CustomEvent(RTPanelTabChangedEvent, {
|
||||||
|
detail: data,
|
||||||
|
});
|
||||||
|
document.dispatchEvent(event);
|
||||||
|
};
|
||||||
|
|
||||||
|
const onRTStructsLoaded = ({ detail }) => {
|
||||||
|
const { rtStructDisplaySet, referencedDisplaySet } = detail;
|
||||||
|
|
||||||
|
const studyMetadata = studyMetadataManager.get(
|
||||||
|
rtStructDisplaySet.StudyInstanceUID
|
||||||
|
);
|
||||||
|
const referencedDisplaysets = studyMetadata.getDerivedDatasets({
|
||||||
|
referencedSeriesInstanceUID: referencedDisplaySet.SeriesInstanceUID,
|
||||||
|
Modality: 'RTSTRUCT',
|
||||||
|
});
|
||||||
|
triggerRTPanelUpdatedEvent({
|
||||||
|
badgeNumber: referencedDisplaysets.length,
|
||||||
|
target: 'rt-panel',
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
document.addEventListener('extensiondicomrtrtloaded', onRTStructsLoaded);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
menuOptions: [
|
menuOptions: [
|
||||||
{
|
{
|
||||||
icon: 'list',
|
icon: 'list',
|
||||||
label: 'RTSTRUCT',
|
label: 'RTSTRUCT',
|
||||||
target: 'rt-panel',
|
target: 'rt-panel',
|
||||||
|
stateEvent: RTPanelTabChangedEvent,
|
||||||
isDisabled: (studies, activeViewport) => {
|
isDisabled: (studies, activeViewport) => {
|
||||||
if (!studies) {
|
if (!studies) {
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -90,11 +90,11 @@ export default async function loadRTStruct(
|
|||||||
const sopInstanceUID = ContourImageSequence
|
const sopInstanceUID = ContourImageSequence
|
||||||
? ContourImageSequence.ReferencedSOPInstanceUID
|
? ContourImageSequence.ReferencedSOPInstanceUID
|
||||||
: _getClosestSOPInstanceUID(
|
: _getClosestSOPInstanceUID(
|
||||||
ContourData,
|
ContourData,
|
||||||
ContourGeometricType,
|
ContourGeometricType,
|
||||||
NumberOfContourPoints,
|
NumberOfContourPoints,
|
||||||
imageIdSopInstanceUidPairs
|
imageIdSopInstanceUidPairs
|
||||||
);
|
);
|
||||||
const imageId = _getImageId(imageIdSopInstanceUidPairs, sopInstanceUID);
|
const imageId = _getImageId(imageIdSopInstanceUidPairs, sopInstanceUID);
|
||||||
|
|
||||||
if (!imageId) {
|
if (!imageId) {
|
||||||
@@ -171,7 +171,13 @@ export default async function loadRTStruct(
|
|||||||
* allows us to easily watch the module or the rtstruct loading process in any other component
|
* allows us to easily watch the module or the rtstruct loading process in any other component
|
||||||
* without subscribing to external events.
|
* without subscribing to external events.
|
||||||
*/
|
*/
|
||||||
const event = new CustomEvent('extensiondicomrtrtloaded');
|
const event = new CustomEvent('extensiondicomrtrtloaded', {
|
||||||
|
detail: {
|
||||||
|
rtStructDisplaySet,
|
||||||
|
referencedDisplaySet,
|
||||||
|
studies,
|
||||||
|
},
|
||||||
|
});
|
||||||
document.dispatchEvent(event);
|
document.dispatchEvent(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -299,7 +305,9 @@ const _getImageId = (imageIdSopInstanceUidPairs, sopInstanceUID) => {
|
|||||||
imageIdSopInstanceUidPairsEntry.sopInstanceUID === sopInstanceUID
|
imageIdSopInstanceUidPairsEntry.sopInstanceUID === sopInstanceUID
|
||||||
);
|
);
|
||||||
|
|
||||||
return imageIdSopInstanceUidPairsEntry ? imageIdSopInstanceUidPairsEntry.imageId : null;
|
return imageIdSopInstanceUidPairsEntry
|
||||||
|
? imageIdSopInstanceUidPairsEntry.imageId
|
||||||
|
: null;
|
||||||
};
|
};
|
||||||
|
|
||||||
function _getImageIdSopInstanceUidPairsForDisplaySet(
|
function _getImageIdSopInstanceUidPairsForDisplaySet(
|
||||||
|
|||||||
@@ -3,6 +3,132 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
## [0.6.11](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-segmentation@0.6.10...@ohif/extension-dicom-segmentation@0.6.11) (2021-09-03)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-dicom-segmentation
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [0.6.10](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-segmentation@0.6.9...@ohif/extension-dicom-segmentation@0.6.10) (2021-06-02)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **dicom-html:** Add parsed dicom meta info section ([#2419](https://github.com/OHIF/Viewers/issues/2419)) ([403688b](https://github.com/OHIF/Viewers/commit/403688b18c52468fc1101166ec0c1734fb710039))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [0.6.9](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-segmentation@0.6.8...@ohif/extension-dicom-segmentation@0.6.9) (2021-05-17)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-dicom-segmentation
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [0.6.8](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-segmentation@0.6.7...@ohif/extension-dicom-segmentation@0.6.8) (2021-04-22)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-dicom-segmentation
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [0.6.7](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-segmentation@0.6.6...@ohif/extension-dicom-segmentation@0.6.7) (2021-04-22)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-dicom-segmentation
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [0.6.6](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-segmentation@0.6.5...@ohif/extension-dicom-segmentation@0.6.6) (2021-04-21)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-dicom-segmentation
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [0.6.5](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-segmentation@0.6.4...@ohif/extension-dicom-segmentation@0.6.5) (2021-04-21)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-dicom-segmentation
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [0.6.4](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-segmentation@0.6.3...@ohif/extension-dicom-segmentation@0.6.4) (2021-04-16)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-dicom-segmentation
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [0.6.3](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-segmentation@0.6.2...@ohif/extension-dicom-segmentation@0.6.3) (2021-04-15)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-dicom-segmentation
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [0.6.2](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-segmentation@0.6.1...@ohif/extension-dicom-segmentation@0.6.2) (2021-03-31)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-dicom-segmentation
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [0.6.1](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-segmentation@0.6.0...@ohif/extension-dicom-segmentation@0.6.1) (2021-03-09)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-dicom-segmentation
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# [0.6.0](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-segmentation@0.5.4...@ohif/extension-dicom-segmentation@0.6.0) (2021-03-04)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **log:** add new log service ([14d6454](https://github.com/OHIF/Viewers/commit/14d6454eafaa2ccb50e133c2945c9558052ea27e))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [0.5.4](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-segmentation@0.5.3...@ohif/extension-dicom-segmentation@0.5.4) (2021-03-03)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-dicom-segmentation
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [0.5.3](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-segmentation@0.5.2...@ohif/extension-dicom-segmentation@0.5.3) (2021-02-25)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-dicom-segmentation
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [0.5.2](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-segmentation@0.5.1...@ohif/extension-dicom-segmentation@0.5.2) (2021-02-05)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-dicom-segmentation
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## [0.5.1](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-segmentation@0.5.0...@ohif/extension-dicom-segmentation@0.5.1) (2021-01-21)
|
## [0.5.1](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-segmentation@0.5.0...@ohif/extension-dicom-segmentation@0.5.1) (2021-01-21)
|
||||||
|
|
||||||
**Note:** Version bump only for package @ohif/extension-dicom-segmentation
|
**Note:** Version bump only for package @ohif/extension-dicom-segmentation
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@ohif/extension-dicom-segmentation",
|
"name": "@ohif/extension-dicom-segmentation",
|
||||||
"version": "0.5.1",
|
"version": "0.6.11",
|
||||||
"description": "OHIF extension for viewing segmentations in the 2D MPR view",
|
"description": "OHIF extension for viewing segmentations in the 2D MPR view",
|
||||||
"author": "OHIF",
|
"author": "OHIF",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
@@ -31,7 +31,7 @@
|
|||||||
"@ohif/core": "^0.50.0",
|
"@ohif/core": "^0.50.0",
|
||||||
"cornerstone-core": "^2.2.8",
|
"cornerstone-core": "^2.2.8",
|
||||||
"cornerstone-tools": "^4.20.1",
|
"cornerstone-tools": "^4.20.1",
|
||||||
"dcmjs": "0.17.0",
|
"dcmjs": "0.18.8",
|
||||||
"prop-types": "^15.6.2",
|
"prop-types": "^15.6.2",
|
||||||
"react": "^16.8.6",
|
"react": "^16.8.6",
|
||||||
"react-dom": "^16.8.6"
|
"react-dom": "^16.8.6"
|
||||||
|
|||||||
+44
-5
@@ -155,7 +155,9 @@ const SegmentationPanel = ({
|
|||||||
|
|
||||||
const brushStackState = getBrushStackState();
|
const brushStackState = getBrushStackState();
|
||||||
brushStackState.activeLabelmapIndex = newLabelmapIndex;
|
brushStackState.activeLabelmapIndex = newLabelmapIndex;
|
||||||
setState(state => ({ ...state, selectedSegmentation }));
|
if (selectedSegmentation) {
|
||||||
|
setState(state => ({ ...state, selectedSegmentation }));
|
||||||
|
}
|
||||||
|
|
||||||
refreshViewports();
|
refreshViewports();
|
||||||
|
|
||||||
@@ -189,6 +191,10 @@ const SegmentationPanel = ({
|
|||||||
'extensiondicomsegmentationsegloaded',
|
'extensiondicomsegmentationsegloaded',
|
||||||
refreshSegmentations
|
refreshSegmentations
|
||||||
);
|
);
|
||||||
|
document.addEventListener(
|
||||||
|
'extensiondicomsegmentationsegselected',
|
||||||
|
updateSegmentationComboBox
|
||||||
|
);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* These are specific to each element;
|
* These are specific to each element;
|
||||||
@@ -207,6 +213,10 @@ const SegmentationPanel = ({
|
|||||||
'extensiondicomsegmentationsegloaded',
|
'extensiondicomsegmentationsegloaded',
|
||||||
refreshSegmentations
|
refreshSegmentations
|
||||||
);
|
);
|
||||||
|
document.removeEventListener(
|
||||||
|
'extensiondicomsegmentationsegselected',
|
||||||
|
updateSegmentationComboBox
|
||||||
|
);
|
||||||
cornerstoneTools.store.state.enabledElements.forEach(enabledElement =>
|
cornerstoneTools.store.state.enabledElements.forEach(enabledElement =>
|
||||||
enabledElement.removeEventListener(
|
enabledElement.removeEventListener(
|
||||||
'cornerstonetoolslabelmapmodified',
|
'cornerstonetoolslabelmapmodified',
|
||||||
@@ -214,7 +224,32 @@ const SegmentationPanel = ({
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
}, [activeIndex, viewports]);
|
}, [
|
||||||
|
activeIndex,
|
||||||
|
updateSegmentationComboBox,
|
||||||
|
viewports,
|
||||||
|
]);
|
||||||
|
|
||||||
|
const updateSegmentationComboBox = e => {
|
||||||
|
const index = e.detail.activatedLabelmapIndex;
|
||||||
|
if (index !== -1) {
|
||||||
|
setState(state => ({ ...state, selectedSegmentation: index }));
|
||||||
|
} else {
|
||||||
|
cleanSegmentationComboBox();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const cleanSegmentationComboBox = () => {
|
||||||
|
setState(state => ({
|
||||||
|
...state,
|
||||||
|
segmentsHidden: [],
|
||||||
|
segmentNumbers: [],
|
||||||
|
labelMapList: [],
|
||||||
|
segmentList: [],
|
||||||
|
isDisabled: true,
|
||||||
|
selectedSegmentation: -1,
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
const refreshSegmentations = () => {
|
const refreshSegmentations = () => {
|
||||||
const activeViewport = getActiveViewport();
|
const activeViewport = getActiveViewport();
|
||||||
@@ -277,7 +312,11 @@ const SegmentationPanel = ({
|
|||||||
activeViewport.SeriesInstanceUID
|
activeViewport.SeriesInstanceUID
|
||||||
);
|
);
|
||||||
|
|
||||||
return referencedSegDisplaysets.map((displaySet, index) => {
|
const filteredReferencedSegDisplaysets = referencedSegDisplaysets.filter(
|
||||||
|
segDisplay => segDisplay.loadError !== true
|
||||||
|
);
|
||||||
|
|
||||||
|
return filteredReferencedSegDisplaysets.map((displaySet, index) => {
|
||||||
const {
|
const {
|
||||||
labelmapIndex,
|
labelmapIndex,
|
||||||
originLabelMapIndex,
|
originLabelMapIndex,
|
||||||
@@ -289,7 +328,7 @@ const SegmentationPanel = ({
|
|||||||
/* Map to display representation */
|
/* Map to display representation */
|
||||||
const dateStr = `${SeriesDate}:${SeriesTime}`.split('.')[0];
|
const dateStr = `${SeriesDate}:${SeriesTime}`.split('.')[0];
|
||||||
const date = moment(dateStr, 'YYYYMMDD:HHmmss');
|
const date = moment(dateStr, 'YYYYMMDD:HHmmss');
|
||||||
const displayDate = date.format('ddd, MMM Do YYYY');
|
const displayDate = date.format('ddd, MMM Do YYYY, h:mm:ss a');
|
||||||
const displayDescription = displaySet.SeriesDescription;
|
const displayDescription = displaySet.SeriesDescription;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -709,7 +748,7 @@ const _getReferencedSegDisplaysets = (StudyInstanceUID, SeriesInstanceUID) => {
|
|||||||
referencedDisplaysets.sort((a, b) => {
|
referencedDisplaysets.sort((a, b) => {
|
||||||
const aNumber = Number(`${a.SeriesDate}${a.SeriesTime}`);
|
const aNumber = Number(`${a.SeriesDate}${a.SeriesTime}`);
|
||||||
const bNumber = Number(`${b.SeriesDate}${b.SeriesTime}`);
|
const bNumber = Number(`${b.SeriesDate}${b.SeriesTime}`);
|
||||||
return aNumber - bNumber;
|
return bNumber - aNumber;
|
||||||
});
|
});
|
||||||
|
|
||||||
return referencedDisplaysets;
|
return referencedDisplaysets;
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import Select from 'react-select';
|
|||||||
|
|
||||||
const SegmentationSelect = ({ value, formatOptionLabel, options }) => (
|
const SegmentationSelect = ({ value, formatOptionLabel, options }) => (
|
||||||
<Select
|
<Select
|
||||||
value={value}
|
value={value === undefined ? null : value}
|
||||||
formatOptionLabel={formatOptionLabel}
|
formatOptionLabel={formatOptionLabel}
|
||||||
styles={segmentationSelectStyles}
|
styles={segmentationSelectStyles}
|
||||||
options={options}
|
options={options}
|
||||||
|
|||||||
@@ -55,6 +55,7 @@ export default function getSopClassHandlerModule({ servicesManager }) {
|
|||||||
referencedDisplaySetUID: null, // Assigned when loaded.
|
referencedDisplaySetUID: null, // Assigned when loaded.
|
||||||
labelmapIndex: null, // Assigned when loaded.
|
labelmapIndex: null, // Assigned when loaded.
|
||||||
isLoaded: false,
|
isLoaded: false,
|
||||||
|
loadError: false,
|
||||||
hasOverlapping: false,
|
hasOverlapping: false,
|
||||||
SeriesDate,
|
SeriesDate,
|
||||||
SeriesTime,
|
SeriesTime,
|
||||||
@@ -63,8 +64,8 @@ export default function getSopClassHandlerModule({ servicesManager }) {
|
|||||||
metadata,
|
metadata,
|
||||||
};
|
};
|
||||||
|
|
||||||
segDisplaySet.getSourceDisplaySet = function(studies) {
|
segDisplaySet.getSourceDisplaySet = function(studies, activateLabelMap = true, onDisplaySetLoadFailureHandler) {
|
||||||
return getSourceDisplaySet(studies, segDisplaySet);
|
return getSourceDisplaySet(studies, segDisplaySet, activateLabelMap, onDisplaySetLoadFailureHandler);
|
||||||
};
|
};
|
||||||
|
|
||||||
segDisplaySet.load = async function(referencedDisplaySet, studies) {
|
segDisplaySet.load = async function(referencedDisplaySet, studies) {
|
||||||
@@ -82,57 +83,50 @@ export default function getSopClassHandlerModule({ servicesManager }) {
|
|||||||
StudyInstanceUID,
|
StudyInstanceUID,
|
||||||
referencedDisplaySet.SeriesInstanceUID
|
referencedDisplaySet.SeriesInstanceUID
|
||||||
);
|
);
|
||||||
return new Promise(async (resolve, reject) => {
|
|
||||||
let results;
|
|
||||||
try {
|
|
||||||
results = _parseSeg(segArrayBuffer, imageIds);
|
|
||||||
} catch (error) {
|
|
||||||
segDisplaySet.isLoaded = false;
|
|
||||||
segDisplaySet.loadError = true;
|
|
||||||
reject(error);
|
|
||||||
}
|
|
||||||
const {
|
|
||||||
labelmapBufferArray,
|
|
||||||
segMetadata,
|
|
||||||
segmentsOnFrame,
|
|
||||||
segmentsOnFrameArray,
|
|
||||||
} = results;
|
|
||||||
|
|
||||||
let labelmapIndex;
|
const results = await _parseSeg(segArrayBuffer, imageIds);
|
||||||
if (labelmapBufferArray.length > 1) {
|
if (results === undefined) {
|
||||||
let labelmapIndexes = [];
|
return;
|
||||||
for (let i = 0; i < labelmapBufferArray.length; ++i) {
|
}
|
||||||
labelmapIndexes.push(
|
const {
|
||||||
await loadSegmentation(
|
labelmapBufferArray,
|
||||||
imageIds,
|
segMetadata,
|
||||||
segDisplaySet,
|
segmentsOnFrame,
|
||||||
labelmapBufferArray[i],
|
segmentsOnFrameArray,
|
||||||
segMetadata,
|
} = results;
|
||||||
segmentsOnFrame,
|
let labelmapIndex;
|
||||||
segmentsOnFrameArray[i]
|
if (labelmapBufferArray.length > 1) {
|
||||||
)
|
let labelmapIndexes = [];
|
||||||
);
|
for (let i = 0; i < labelmapBufferArray.length; ++i) {
|
||||||
}
|
labelmapIndexes.push(
|
||||||
/**
|
await loadSegmentation(
|
||||||
* Since overlapping segmentations have virtual labelmaps,
|
imageIds,
|
||||||
* originLabelMapIndex is used in the panel to select the correct dropdown value.
|
segDisplaySet,
|
||||||
*/
|
labelmapBufferArray[i],
|
||||||
segDisplaySet.hasOverlapping = true;
|
segMetadata,
|
||||||
segDisplaySet.originLabelMapIndex = labelmapIndexes[0];
|
segmentsOnFrame,
|
||||||
labelmapIndex = labelmapIndexes[0];
|
segmentsOnFrameArray[i]
|
||||||
console.warn('Overlapping segmentations!');
|
)
|
||||||
} else {
|
|
||||||
labelmapIndex = await loadSegmentation(
|
|
||||||
imageIds,
|
|
||||||
segDisplaySet,
|
|
||||||
labelmapBufferArray[0],
|
|
||||||
segMetadata,
|
|
||||||
segmentsOnFrame,
|
|
||||||
[]
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
resolve(labelmapIndex);
|
/**
|
||||||
});
|
* Since overlapping segments have virtual labelmaps,
|
||||||
|
* originLabelMapIndex is used in the panel to select the correct dropdown value.
|
||||||
|
*/
|
||||||
|
segDisplaySet.hasOverlapping = true;
|
||||||
|
segDisplaySet.originLabelMapIndex = labelmapIndexes[0];
|
||||||
|
labelmapIndex = labelmapIndexes[0];
|
||||||
|
console.warn('Overlapping segments!');
|
||||||
|
} else {
|
||||||
|
labelmapIndex = await loadSegmentation(
|
||||||
|
imageIds,
|
||||||
|
segDisplaySet,
|
||||||
|
labelmapBufferArray[0],
|
||||||
|
segMetadata,
|
||||||
|
segmentsOnFrame,
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return segDisplaySet;
|
return segDisplaySet;
|
||||||
|
|||||||
@@ -1,88 +1,16 @@
|
|||||||
import setActiveLabelmap from './utils/setActiveLabelMap';
|
import setActiveLabelmap from './utils/setActiveLabelMap';
|
||||||
import { classes } from '@ohif/core';
|
import { metadata } from '@ohif/core';
|
||||||
|
|
||||||
const { ImageSet } = classes;
|
export default function getSourceDisplaySet(studies, segDisplaySet, activateLabelMap = true, onDisplaySetLoadFailureHandler) {
|
||||||
|
const referencedDisplaySet = metadata.StudyMetadata.getReferencedDisplaySet(segDisplaySet, studies);
|
||||||
|
|
||||||
export default function getSourceDisplaySet(studies, segDisplaySet) {
|
let activatedLabelmapPromise;
|
||||||
const referencedDisplaySet = _getReferencedDisplaySet(segDisplaySet, studies);
|
if (activateLabelMap) {
|
||||||
|
activatedLabelmapPromise = setActiveLabelmap(referencedDisplaySet, studies, segDisplaySet, undefined, onDisplaySetLoadFailureHandler);
|
||||||
setActiveLabelmap(referencedDisplaySet, studies, segDisplaySet);
|
|
||||||
|
|
||||||
return referencedDisplaySet;
|
|
||||||
}
|
|
||||||
|
|
||||||
const _getReferencedDisplaySet = (segDisplaySet, studies) => {
|
|
||||||
let allDisplaySets = [];
|
|
||||||
|
|
||||||
studies.forEach(study => {
|
|
||||||
allDisplaySets = allDisplaySets.concat(study.displaySets);
|
|
||||||
});
|
|
||||||
|
|
||||||
const otherDisplaySets = allDisplaySets.filter(
|
|
||||||
ds => ds.displaySetInstanceUID !== segDisplaySet.displaySetInstanceUID
|
|
||||||
);
|
|
||||||
|
|
||||||
const { metadata } = segDisplaySet;
|
|
||||||
|
|
||||||
let referencedSeriesInstanceUIDs;
|
|
||||||
|
|
||||||
if (metadata.ReferencedSeriesSequence) {
|
|
||||||
const ReferencedSeriesSequence = _toArray(
|
|
||||||
metadata.ReferencedSeriesSequence
|
|
||||||
);
|
|
||||||
|
|
||||||
referencedSeriesInstanceUIDs = ReferencedSeriesSequence.map(
|
|
||||||
ReferencedSeries => ReferencedSeries.SeriesInstanceUID
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
const { PerFrameFunctionalGroupsSequence } = metadata;
|
|
||||||
|
|
||||||
let SourceImageSequence;
|
|
||||||
|
|
||||||
if (metadata.SourceImageSequence) {
|
|
||||||
SourceImageSequence = metadata.SourceImageSequence;
|
|
||||||
} else {
|
|
||||||
const firstFunctionalGroups = _toArray(
|
|
||||||
PerFrameFunctionalGroupsSequence
|
|
||||||
)[0];
|
|
||||||
const { DerivationImageSequence } = firstFunctionalGroups;
|
|
||||||
|
|
||||||
SourceImageSequence = DerivationImageSequence;
|
|
||||||
}
|
|
||||||
|
|
||||||
const firstSourceImage = _toArray(SourceImageSequence)[0];
|
|
||||||
|
|
||||||
const { ReferencedSOPInstanceUID } = firstSourceImage;
|
|
||||||
|
|
||||||
referencedSeriesInstanceUIDs = _findReferencedSeriesInstanceUIDsFromSOPInstanceUID(
|
|
||||||
otherDisplaySets,
|
|
||||||
ReferencedSOPInstanceUID
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const referencedDisplaySet = otherDisplaySets.find(ds =>
|
return {
|
||||||
referencedSeriesInstanceUIDs.includes(ds.SeriesInstanceUID)
|
referencedDisplaySet : referencedDisplaySet,
|
||||||
);
|
activatedLabelmapPromise : activatedLabelmapPromise
|
||||||
|
|
||||||
return referencedDisplaySet;
|
|
||||||
};
|
|
||||||
|
|
||||||
const _findReferencedSeriesInstanceUIDsFromSOPInstanceUID = (
|
|
||||||
displaySets,
|
|
||||||
SOPInstanceUID
|
|
||||||
) => {
|
|
||||||
const imageSets = displaySets.filter(ds => ds instanceof ImageSet);
|
|
||||||
|
|
||||||
for (let i = 0; i < imageSets.length; i++) {
|
|
||||||
const { images } = imageSets[i];
|
|
||||||
for (let j = 0; j < images.length; j++) {
|
|
||||||
if (images[j].SOPInstanceUID === SOPInstanceUID) {
|
|
||||||
return [images[j].getData().metadata.SeriesInstanceUID];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
function _toArray(arrayOrObject) {
|
|
||||||
return Array.isArray(arrayOrObject) ? arrayOrObject : [arrayOrObject];
|
|
||||||
}
|
}
|
||||||
@@ -1,9 +1,12 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import OHIF from '@ohif/core';
|
||||||
|
|
||||||
import init from './init.js';
|
import init from './init.js';
|
||||||
import toolbarModule from './toolbarModule.js';
|
import toolbarModule from './toolbarModule.js';
|
||||||
import getSopClassHandlerModule from './getOHIFDicomSegSopClassHandler.js';
|
import getSopClassHandlerModule from './getOHIFDicomSegSopClassHandler.js';
|
||||||
import SegmentationPanel from './components/SegmentationPanel/SegmentationPanel.js';
|
import SegmentationPanel from './components/SegmentationPanel/SegmentationPanel.js';
|
||||||
import { version } from '../package.json';
|
import { version } from '../package.json';
|
||||||
|
const { studyMetadataManager } = OHIF.utils;
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
/**
|
/**
|
||||||
@@ -25,12 +28,13 @@ export default {
|
|||||||
return toolbarModule;
|
return toolbarModule;
|
||||||
},
|
},
|
||||||
getPanelModule({ commandsManager, api, servicesManager }) {
|
getPanelModule({ commandsManager, api, servicesManager }) {
|
||||||
const { UINotificationService } = servicesManager.services;
|
const { UINotificationService, LoggerService } = servicesManager.services;
|
||||||
|
|
||||||
const ExtendedSegmentationPanel = props => {
|
const ExtendedSegmentationPanel = props => {
|
||||||
const { activeContexts } = api.hooks.useAppContext();
|
const { activeContexts } = api.hooks.useAppContext();
|
||||||
|
|
||||||
const onDisplaySetLoadFailureHandler = error => {
|
const onDisplaySetLoadFailureHandler = error => {
|
||||||
|
LoggerService.error({ error, message: error.message });
|
||||||
UINotificationService.show({
|
UINotificationService.show({
|
||||||
title: 'DICOM Segmentation Loader',
|
title: 'DICOM Segmentation Loader',
|
||||||
message: error.message,
|
message: error.message,
|
||||||
@@ -78,12 +82,50 @@ export default {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const SegmentationPanelTabUpdatedEvent = 'segmentation-panel-tab-updated';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Trigger's an event to update the state of the panel's RoundedButtonGroup.
|
||||||
|
*
|
||||||
|
* This is required to avoid extension state
|
||||||
|
* coupling with the viewer's ToolbarRow component.
|
||||||
|
*
|
||||||
|
* @param {object} data
|
||||||
|
*/
|
||||||
|
const triggerSegmentationPanelTabUpdatedEvent = data => {
|
||||||
|
const event = new CustomEvent(SegmentationPanelTabUpdatedEvent, {
|
||||||
|
detail: data,
|
||||||
|
});
|
||||||
|
document.dispatchEvent(event);
|
||||||
|
};
|
||||||
|
|
||||||
|
const onSegmentationsLoaded = ({ detail }) => {
|
||||||
|
const { segDisplaySet, segMetadata } = detail;
|
||||||
|
const studyMetadata = studyMetadataManager.get(
|
||||||
|
segDisplaySet.StudyInstanceUID
|
||||||
|
);
|
||||||
|
const referencedDisplaysets = studyMetadata.getDerivedDatasets({
|
||||||
|
referencedSeriesInstanceUID: segMetadata.seriesInstanceUid,
|
||||||
|
Modality: 'SEG',
|
||||||
|
});
|
||||||
|
triggerSegmentationPanelTabUpdatedEvent({
|
||||||
|
badgeNumber: referencedDisplaysets.length,
|
||||||
|
target: 'segmentation-panel',
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
document.addEventListener(
|
||||||
|
'extensiondicomsegmentationsegloaded',
|
||||||
|
onSegmentationsLoaded
|
||||||
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
menuOptions: [
|
menuOptions: [
|
||||||
{
|
{
|
||||||
icon: 'list',
|
icon: 'list',
|
||||||
label: 'Segmentations',
|
label: 'Segmentations',
|
||||||
target: 'segmentation-panel',
|
target: 'segmentation-panel',
|
||||||
|
stateEvent: SegmentationPanelTabUpdatedEvent,
|
||||||
isDisabled: studies => {
|
isDisabled: studies => {
|
||||||
if (!studies) {
|
if (!studies) {
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -59,7 +59,16 @@ export default async function loadSegmentation(
|
|||||||
* without subscribing to external events.
|
* without subscribing to external events.
|
||||||
*/
|
*/
|
||||||
console.log('Segmentation loaded.');
|
console.log('Segmentation loaded.');
|
||||||
const event = new CustomEvent('extensiondicomsegmentationsegloaded');
|
const event = new CustomEvent('extensiondicomsegmentationsegloaded', {
|
||||||
|
detail: {
|
||||||
|
imageIds,
|
||||||
|
segDisplaySet,
|
||||||
|
labelmapBuffer,
|
||||||
|
segMetadata,
|
||||||
|
segmentsOnFrame,
|
||||||
|
labelmapSegments,
|
||||||
|
},
|
||||||
|
});
|
||||||
document.dispatchEvent(event);
|
document.dispatchEvent(event);
|
||||||
|
|
||||||
return labelmapIndex;
|
return labelmapIndex;
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ export default async function setActiveLabelmap(
|
|||||||
displaySet,
|
displaySet,
|
||||||
callback = () => {},
|
callback = () => {},
|
||||||
onDisplaySetLoadFailure = err => {
|
onDisplaySetLoadFailure = err => {
|
||||||
throw new Error(err.message);
|
console.error(err.message);
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
const studyMetadata = studyMetadataManager.get(
|
const studyMetadata = studyMetadataManager.get(
|
||||||
@@ -48,16 +48,15 @@ export default async function setActiveLabelmap(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!displaySet.isLoaded) {
|
if (!displaySet.isLoaded) {
|
||||||
const loadPromise = displaySet.load(referencedDisplaySet, studies);
|
try {
|
||||||
|
await displaySet.load(referencedDisplaySet, studies);
|
||||||
loadPromise.catch(error => {
|
} catch (error) {
|
||||||
|
displaySet.isLoaded = false;
|
||||||
|
displaySet.loadError = true;
|
||||||
onDisplaySetLoadFailure(error);
|
onDisplaySetLoadFailure(error);
|
||||||
|
|
||||||
// Return old index.
|
return -1;
|
||||||
return activeLabelmapIndex;
|
}
|
||||||
});
|
|
||||||
|
|
||||||
await loadPromise;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
labelmapIndex =
|
labelmapIndex =
|
||||||
@@ -67,8 +66,11 @@ export default async function setActiveLabelmap(
|
|||||||
|
|
||||||
// This might have just been created, so need to use the non-cached value.
|
// This might have just been created, so need to use the non-cached value.
|
||||||
state = cornerstoneTools.getModule('segmentation').state;
|
state = cornerstoneTools.getModule('segmentation').state;
|
||||||
|
|
||||||
brushStackState = state.series[firstImageId];
|
brushStackState = state.series[firstImageId];
|
||||||
brushStackState.activeLabelmapIndex = labelmapIndex;
|
if (brushStackState) {
|
||||||
|
brushStackState.activeLabelmapIndex = labelmapIndex;
|
||||||
|
}
|
||||||
|
|
||||||
refreshViewports();
|
refreshViewports();
|
||||||
callback();
|
callback();
|
||||||
|
|||||||
@@ -3,6 +3,97 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
## [0.2.11](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-tag-browser@0.2.10...@ohif/extension-dicom-tag-browser@0.2.11) (2021-06-03)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-dicom-tag-browser
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [0.2.10](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-tag-browser@0.2.9...@ohif/extension-dicom-tag-browser@0.2.10) (2021-06-02)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **dicom-html:** Add parsed dicom meta info section ([#2419](https://github.com/OHIF/Viewers/issues/2419)) ([403688b](https://github.com/OHIF/Viewers/commit/403688b18c52468fc1101166ec0c1734fb710039))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [0.2.9](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-tag-browser@0.2.8...@ohif/extension-dicom-tag-browser@0.2.9) (2021-04-22)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-dicom-tag-browser
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [0.2.8](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-tag-browser@0.2.7...@ohif/extension-dicom-tag-browser@0.2.8) (2021-04-22)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-dicom-tag-browser
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [0.2.7](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-tag-browser@0.2.6...@ohif/extension-dicom-tag-browser@0.2.7) (2021-04-21)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-dicom-tag-browser
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [0.2.6](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-tag-browser@0.2.5...@ohif/extension-dicom-tag-browser@0.2.6) (2021-04-15)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-dicom-tag-browser
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [0.2.5](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-tag-browser@0.2.4...@ohif/extension-dicom-tag-browser@0.2.5) (2021-03-09)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-dicom-tag-browser
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [0.2.4](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-tag-browser@0.2.3...@ohif/extension-dicom-tag-browser@0.2.4) (2021-03-03)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-dicom-tag-browser
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [0.2.3](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-tag-browser@0.2.2...@ohif/extension-dicom-tag-browser@0.2.3) (2021-02-25)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-dicom-tag-browser
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [0.2.2](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-tag-browser@0.2.1...@ohif/extension-dicom-tag-browser@0.2.2) (2021-02-05)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-dicom-tag-browser
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [0.2.1](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-tag-browser@0.2.0...@ohif/extension-dicom-tag-browser@0.2.1) (2021-02-05)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-dicom-tag-browser
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# [0.2.0](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-tag-browser@0.1.1...@ohif/extension-dicom-tag-browser@0.2.0) (2020-12-10)
|
# [0.2.0](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-tag-browser@0.1.1...@ohif/extension-dicom-tag-browser@0.2.0) (2020-12-10)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@ohif/extension-dicom-tag-browser",
|
"name": "@ohif/extension-dicom-tag-browser",
|
||||||
"version": "0.2.0",
|
"version": "0.2.11",
|
||||||
"description": "OHIF extension for checking DICOM headers.",
|
"description": "OHIF extension for checking DICOM headers.",
|
||||||
"author": "OHIF",
|
"author": "OHIF",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
@@ -28,12 +28,12 @@
|
|||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@ohif/core": "^2.6.0",
|
"@ohif/core": "^2.6.0",
|
||||||
"dcmjs": "0.17.0",
|
"dcmjs": "0.18.8",
|
||||||
"react": "^16.8.6"
|
"react": "^16.8.6"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "^7.5.5",
|
"@babel/runtime": "^7.5.5",
|
||||||
"dicomweb-client": "^0.6.0",
|
"dicomweb-client": "^0.8.1",
|
||||||
"moment": "2.24.0",
|
"moment": "2.24.0",
|
||||||
"react-select": "^3.0.8"
|
"react-select": "^3.0.8"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,11 @@
|
|||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.dicom-tag-browser-table-wrapper {
|
||||||
|
height: 500px;
|
||||||
|
overflow-y: scroll;
|
||||||
|
}
|
||||||
|
|
||||||
.dicom-tag-browser-table tr {
|
.dicom-tag-browser-table tr {
|
||||||
padding-left: 10px;
|
padding-left: 10px;
|
||||||
padding-right: 10px;
|
padding-right: 10px;
|
||||||
@@ -11,6 +16,20 @@
|
|||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.dicom-tag-browser-content {
|
||||||
|
overflow: hidden;
|
||||||
|
width: 100%;
|
||||||
|
padding-bottom: 50px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dicom-tag-browser-instance-range .range {
|
||||||
|
height: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dicom-tag-browser-instance-range {
|
||||||
|
padding: 20px 0 20px 0;
|
||||||
|
}
|
||||||
|
|
||||||
.dicom-tag-browser-table td.dicom-tag-browser-table-center {
|
.dicom-tag-browser-table td.dicom-tag-browser-table-center {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
import { classes, cornerstone as OHIFCornerstone } from '@ohif/core';
|
import { classes, cornerstone as OHIFCornerstone } from '@ohif/core';
|
||||||
|
import { Range } from '@ohif/ui';
|
||||||
import dcmjs from 'dcmjs';
|
import dcmjs from 'dcmjs';
|
||||||
import DicomBrowserSelect from './DicomBrowserSelect';
|
import DicomBrowserSelect from './DicomBrowserSelect';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
@@ -99,26 +100,35 @@ const DicomTagBrowser = ({ displaySets, displaySetInstanceUID }) => {
|
|||||||
let instanceSelectList = null;
|
let instanceSelectList = null;
|
||||||
|
|
||||||
if (isImageStack) {
|
if (isImageStack) {
|
||||||
const selectedInstanceValue = instanceList[activeInstance];
|
|
||||||
|
|
||||||
instanceSelectList = (
|
instanceSelectList = (
|
||||||
<DicomBrowserSelect
|
<div className="dicom-tag-browser-instance-range">
|
||||||
value={selectedInstanceValue}
|
<Range
|
||||||
formatOptionLabel={DicomBrowserSelectItem}
|
showValue
|
||||||
options={instanceList}
|
step={1}
|
||||||
/>
|
min={1}
|
||||||
|
max={instanceList.length - 1}
|
||||||
|
value={activeInstance}
|
||||||
|
valueRenderer={value => <p>Instance Number: {value}</p>}
|
||||||
|
onChange={({ target }) => {
|
||||||
|
const instanceIndex = parseInt(target.value);
|
||||||
|
setActiveInstance(instanceIndex);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div className="dicom-tag-browser-content">
|
||||||
<DicomBrowserSelect
|
<DicomBrowserSelect
|
||||||
value={selectedDisplaySetValue}
|
value={selectedDisplaySetValue}
|
||||||
formatOptionLabel={DicomBrowserSelectItem}
|
formatOptionLabel={DicomBrowserSelectItem}
|
||||||
options={displaySetList}
|
options={displaySetList}
|
||||||
/>
|
/>
|
||||||
{instanceSelectList}
|
{instanceSelectList}
|
||||||
<DicomTagTable tags={tags} meta={meta}></DicomTagTable>
|
<div className="dicom-tag-browser-table-wrapper">
|
||||||
|
<DicomTagTable tags={tags} meta={meta}></DicomTagTable>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -127,30 +137,28 @@ function DicomTagTable({ tags, meta }) {
|
|||||||
const rows = getFormattedRowsFromTags(tags, meta);
|
const rows = getFormattedRowsFromTags(tags, meta);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<table className="dicom-tag-browser-table">
|
||||||
<table className="dicom-tag-browser-table">
|
<tbody>
|
||||||
<tbody>
|
<tr>
|
||||||
<tr>
|
<th className="dicom-tag-browser-table-left">Tag</th>
|
||||||
<th className="dicom-tag-browser-table-left">Tag</th>
|
<th className="dicom-tag-browser-table-left">Value Representation</th>
|
||||||
<th className="dicom-tag-browser-table-left">Value Representation</th>
|
<th className="dicom-tag-browser-table-left">Keyword</th>
|
||||||
<th className="dicom-tag-browser-table-left">Keyword</th>
|
<th className="dicom-tag-browser-table-left">Value</th>
|
||||||
<th className="dicom-tag-browser-table-left">Value</th>
|
</tr>
|
||||||
</tr>
|
{rows.map((row, index) => {
|
||||||
{rows.map((row, index) => {
|
const className = row.className ? row.className : null;
|
||||||
const className = row.className ? row.className : null;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<tr className={className} key={`DICOMTagRow-${index}`}>
|
<tr className={className} key={`DICOMTagRow-${index}`}>
|
||||||
<td>{row[0]}</td>
|
<td>{row[0]}</td>
|
||||||
<td className="dicom-tag-browser-table-center">{row[1]}</td>
|
<td className="dicom-tag-browser-table-center">{row[1]}</td>
|
||||||
<td>{row[2]}</td>
|
<td>{row[2]}</td>
|
||||||
<td>{row[3]}</td>
|
<td>{row[3]}</td>
|
||||||
</tr>
|
</tr>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -183,12 +191,16 @@ function getFormattedRowsFromTags(tags, meta) {
|
|||||||
} else {
|
} else {
|
||||||
if (tagInfo.vr === 'xs') {
|
if (tagInfo.vr === 'xs') {
|
||||||
try {
|
try {
|
||||||
const dataset = metadataProvider.getStudyDataset(meta.StudyInstanceUID);
|
const dataset = metadataProvider.getStudyDataset(
|
||||||
|
meta.StudyInstanceUID
|
||||||
|
);
|
||||||
const tag = dcmjs.data.Tag.fromPString(tagInfo.tag).toCleanString();
|
const tag = dcmjs.data.Tag.fromPString(tagInfo.tag).toCleanString();
|
||||||
const originalTagInfo = dataset[tag];
|
const originalTagInfo = dataset[tag];
|
||||||
tagInfo.vr = originalTagInfo.vr;
|
tagInfo.vr = originalTagInfo.vr;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(`Failed to parse value representation for tag '${tagInfo.keyword}'`);
|
console.error(
|
||||||
|
`Failed to parse value representation for tag '${tagInfo.keyword}'`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ export default function getCommandsModule(servicesManager) {
|
|||||||
|
|
||||||
const { UIModalService } = servicesManager.services;
|
const { UIModalService } = servicesManager.services;
|
||||||
|
|
||||||
const WrappedDicomTagBrowser = function () {
|
const WrappedDicomTagBrowser = function() {
|
||||||
return (
|
return (
|
||||||
<DicomTagBrowser
|
<DicomTagBrowser
|
||||||
displaySets={displaySets}
|
displaySets={displaySets}
|
||||||
@@ -34,7 +34,7 @@ export default function getCommandsModule(servicesManager) {
|
|||||||
content: WrappedDicomTagBrowser,
|
content: WrappedDicomTagBrowser,
|
||||||
title: `DICOM Tag Browser`,
|
title: `DICOM Tag Browser`,
|
||||||
fullscreen: true,
|
fullscreen: true,
|
||||||
showScrollbar: true
|
noScroll: true,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -3,6 +3,183 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
## [1.11.16](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.11.15...@ohif/extension-vtk@1.11.16) (2021-09-03)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-vtk
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [1.11.15](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.11.14...@ohif/extension-vtk@1.11.15) (2021-08-30)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* MPR metadata parsing for spacings ([#2517](https://github.com/OHIF/Viewers/issues/2517)) ([715dfc5](https://github.com/OHIF/Viewers/commit/715dfc556a3883de31d74012dcc99e431d91f57d))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [1.11.14](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.11.13...@ohif/extension-vtk@1.11.14) (2021-06-03)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-vtk
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [1.11.13](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.11.12...@ohif/extension-vtk@1.11.13) (2021-06-02)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **dicom-html:** Add parsed dicom meta info section ([#2419](https://github.com/OHIF/Viewers/issues/2419)) ([403688b](https://github.com/OHIF/Viewers/commit/403688b18c52468fc1101166ec0c1734fb710039))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [1.11.12](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.11.11...@ohif/extension-vtk@1.11.12) (2021-05-17)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-vtk
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [1.11.11](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.11.10...@ohif/extension-vtk@1.11.11) (2021-04-22)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-vtk
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [1.11.10](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.11.9...@ohif/extension-vtk@1.11.10) (2021-04-22)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-vtk
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [1.11.9](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.11.8...@ohif/extension-vtk@1.11.9) (2021-04-21)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-vtk
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [1.11.8](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.11.7...@ohif/extension-vtk@1.11.8) (2021-04-21)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-vtk
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [1.11.7](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.11.6...@ohif/extension-vtk@1.11.7) (2021-04-21)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-vtk
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [1.11.6](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.11.5...@ohif/extension-vtk@1.11.6) (2021-04-16)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-vtk
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [1.11.5](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.11.4...@ohif/extension-vtk@1.11.5) (2021-04-15)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-vtk
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [1.11.4](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.11.3...@ohif/extension-vtk@1.11.4) (2021-03-26)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-vtk
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [1.11.3](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.11.2...@ohif/extension-vtk@1.11.3) (2021-03-25)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-vtk
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [1.11.2](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.11.1...@ohif/extension-vtk@1.11.2) (2021-03-09)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-vtk
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [1.11.1](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.11.0...@ohif/extension-vtk@1.11.1) (2021-03-09)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-vtk
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# [1.11.0](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.10.6...@ohif/extension-vtk@1.11.0) (2021-03-04)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **log:** add new log service ([14d6454](https://github.com/OHIF/Viewers/commit/14d6454eafaa2ccb50e133c2945c9558052ea27e))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [1.10.6](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.10.5...@ohif/extension-vtk@1.10.6) (2021-03-03)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-vtk
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [1.10.5](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.10.4...@ohif/extension-vtk@1.10.5) (2021-02-25)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-vtk
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [1.10.4](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.10.3...@ohif/extension-vtk@1.10.4) (2021-02-05)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-vtk
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [1.10.3](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.10.2...@ohif/extension-vtk@1.10.3) (2021-02-05)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-vtk
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## [1.10.2](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.10.1...@ohif/extension-vtk@1.10.2) (2021-01-21)
|
## [1.10.2](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.10.1...@ohif/extension-vtk@1.10.2) (2021-01-21)
|
||||||
|
|
||||||
**Note:** Version bump only for package @ohif/extension-vtk
|
**Note:** Version bump only for package @ohif/extension-vtk
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@ohif/extension-vtk",
|
"name": "@ohif/extension-vtk",
|
||||||
"version": "1.10.2",
|
"version": "1.11.16",
|
||||||
"description": "OHIF extension for VTK.js",
|
"description": "OHIF extension for VTK.js",
|
||||||
"author": "OHIF",
|
"author": "OHIF",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
@@ -35,7 +35,7 @@
|
|||||||
"cornerstone-core": "^2.2.8",
|
"cornerstone-core": "^2.2.8",
|
||||||
"cornerstone-tools": "^4.20.1",
|
"cornerstone-tools": "^4.20.1",
|
||||||
"cornerstone-wado-image-loader": "^3.1.0",
|
"cornerstone-wado-image-loader": "^3.1.0",
|
||||||
"dcmjs": "0.17.0",
|
"dcmjs": "0.18.8",
|
||||||
"dicom-parser": "^1.8.3",
|
"dicom-parser": "^1.8.3",
|
||||||
"i18next": "^17.0.3",
|
"i18next": "^17.0.3",
|
||||||
"i18next-browser-languagedetector": "^3.0.1",
|
"i18next-browser-languagedetector": "^3.0.1",
|
||||||
@@ -53,8 +53,8 @@
|
|||||||
"react-vtkjs-viewport": "^0.14.2"
|
"react-vtkjs-viewport": "^0.14.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@ohif/core": "^2.12.1",
|
"@ohif/core": "^2.13.15",
|
||||||
"@ohif/ui": "^1.8.1",
|
"@ohif/ui": "^1.9.3",
|
||||||
"cornerstone-tools": "^4.20.1",
|
"cornerstone-tools": "^4.20.1",
|
||||||
"cornerstone-wado-image-loader": "^3.1.0",
|
"cornerstone-wado-image-loader": "^3.1.0",
|
||||||
"dicom-parser": "^1.8.3",
|
"dicom-parser": "^1.8.3",
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ class OHIFVTKViewport extends Component {
|
|||||||
};
|
};
|
||||||
|
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
onScroll: () => {},
|
onScroll: () => { },
|
||||||
};
|
};
|
||||||
|
|
||||||
static id = 'OHIFVTKViewport';
|
static id = 'OHIFVTKViewport';
|
||||||
@@ -271,7 +271,7 @@ class OHIFVTKViewport extends Component {
|
|||||||
|
|
||||||
volumeMapper.setSampleDistance(sampleDistance);
|
volumeMapper.setSampleDistance(sampleDistance);
|
||||||
|
|
||||||
// Be generous to surpress warnings, as the logging really hurts performance.
|
// Be generous to suppress warnings, as the logging really hurts performance.
|
||||||
// TODO: maybe we should auto adjust samples to 1000.
|
// TODO: maybe we should auto adjust samples to 1000.
|
||||||
volumeMapper.setMaximumSamplesPerRay(4000);
|
volumeMapper.setMaximumSamplesPerRay(4000);
|
||||||
|
|
||||||
@@ -360,12 +360,15 @@ class OHIFVTKViewport extends Component {
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
const errorTitle = 'Failed to load 2D MPR';
|
const errorTitle = 'Failed to load 2D MPR';
|
||||||
console.error(errorTitle, error);
|
console.error(errorTitle, error);
|
||||||
const { UINotificationService } = this.props.servicesManager.services;
|
const {
|
||||||
|
UINotificationService,
|
||||||
|
LoggerService,
|
||||||
|
} = this.props.servicesManager.services;
|
||||||
if (this.props.viewportIndex === 0) {
|
if (this.props.viewportIndex === 0) {
|
||||||
const message = error.message.includes('buffer')
|
const message = error.message.includes('buffer')
|
||||||
? 'Dataset is too big to display in MPR'
|
? 'Dataset is too big to display in MPR'
|
||||||
: error.message;
|
: error.message;
|
||||||
console.error(errorTitle, error);
|
LoggerService.error({ error, message });
|
||||||
UINotificationService.show({
|
UINotificationService.show({
|
||||||
title: errorTitle,
|
title: errorTitle,
|
||||||
message,
|
message,
|
||||||
@@ -395,7 +398,7 @@ class OHIFVTKViewport extends Component {
|
|||||||
|
|
||||||
if (
|
if (
|
||||||
displaySet.displaySetInstanceUID !==
|
displaySet.displaySetInstanceUID !==
|
||||||
prevDisplaySet.displaySetInstanceUID ||
|
prevDisplaySet.displaySetInstanceUID ||
|
||||||
displaySet.SOPInstanceUID !== prevDisplaySet.SOPInstanceUID ||
|
displaySet.SOPInstanceUID !== prevDisplaySet.SOPInstanceUID ||
|
||||||
displaySet.frameIndex !== prevDisplaySet.frameIndex
|
displaySet.frameIndex !== prevDisplaySet.frameIndex
|
||||||
) {
|
) {
|
||||||
@@ -428,11 +431,15 @@ class OHIFVTKViewport extends Component {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const onPixelDataInsertedErrorCallback = error => {
|
const onPixelDataInsertedErrorCallback = error => {
|
||||||
const { UINotificationService } = this.props.servicesManager.services;
|
const {
|
||||||
|
UINotificationService,
|
||||||
|
LoggerService,
|
||||||
|
} = this.props.servicesManager.services;
|
||||||
|
|
||||||
if (!this.hasError) {
|
if (!this.hasError) {
|
||||||
if (this.props.viewportIndex === 0) {
|
if (this.props.viewportIndex === 0) {
|
||||||
// Only show the notification from one viewport 1 in MPR2D.
|
// Only show the notification from one viewport 1 in MPR2D.
|
||||||
|
LoggerService.error({ error, message: error.message });
|
||||||
UINotificationService.show({
|
UINotificationService.show({
|
||||||
title: 'MPR Load Error',
|
title: 'MPR Load Error',
|
||||||
message: error.message,
|
message: error.message,
|
||||||
|
|||||||
@@ -14,7 +14,9 @@ import OHIFVTKViewport from './OHIFVTKViewport';
|
|||||||
|
|
||||||
const { BlendMode } = Constants;
|
const { BlendMode } = Constants;
|
||||||
|
|
||||||
const commandsModule = ({ commandsManager, UINotificationService }) => {
|
const commandsModule = ({ commandsManager, servicesManager }) => {
|
||||||
|
const { UINotificationService, LoggerService } = servicesManager.services;
|
||||||
|
|
||||||
// TODO: Put this somewhere else
|
// TODO: Put this somewhere else
|
||||||
let apis = {};
|
let apis = {};
|
||||||
let defaultVOI;
|
let defaultVOI;
|
||||||
@@ -175,7 +177,7 @@ const commandsModule = ({ commandsManager, UINotificationService }) => {
|
|||||||
segmentNumber,
|
segmentNumber,
|
||||||
frameIndex,
|
frameIndex,
|
||||||
frame,
|
frame,
|
||||||
done = () => { },
|
done = () => {},
|
||||||
}) => {
|
}) => {
|
||||||
let api = apis[viewports.activeViewportIndex];
|
let api = apis[viewports.activeViewportIndex];
|
||||||
|
|
||||||
@@ -473,10 +475,12 @@ const commandsModule = ({ commandsManager, UINotificationService }) => {
|
|||||||
const volumeLength = dimensions[0] * dimensions[1] * dimensions[2];
|
const volumeLength = dimensions[0] * dimensions[1] * dimensions[2];
|
||||||
|
|
||||||
if (volumeLength > maxBufferLengthFloat32) {
|
if (volumeLength > maxBufferLengthFloat32) {
|
||||||
|
const message =
|
||||||
|
'This volume is too large to fit in WebGL 1 textures and will display incorrectly. Please use a different browser to view this data';
|
||||||
|
LoggerService.error({ message });
|
||||||
UINotificationService.show({
|
UINotificationService.show({
|
||||||
title: 'Browser does not support WebGL 2',
|
title: 'Browser does not support WebGL 2',
|
||||||
message:
|
message,
|
||||||
'This volume is too large to fit in WebGL 1 textures and will display incorrectly. Please use a different browser to view this data',
|
|
||||||
type: 'error',
|
type: 'error',
|
||||||
autoClose: false,
|
autoClose: false,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -35,8 +35,7 @@ const vtkExtension = {
|
|||||||
return toolbarModule;
|
return toolbarModule;
|
||||||
},
|
},
|
||||||
getCommandsModule({ commandsManager, servicesManager }) {
|
getCommandsModule({ commandsManager, servicesManager }) {
|
||||||
const { UINotificationService } = servicesManager.services;
|
return commandsModule({ commandsManager, servicesManager });
|
||||||
return commandsModule({ commandsManager, UINotificationService });
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -31,63 +31,7 @@ const _isDisplaySetReconstructable = (viewportSpecificData = {}, activeViewportI
|
|||||||
|
|
||||||
if (!displaySet) {
|
if (!displaySet) {
|
||||||
return false;
|
return false;
|
||||||
}
|
};
|
||||||
|
|
||||||
// 2D MPR is not currently available for 4D datasets.
|
|
||||||
|
|
||||||
// Assuming that slices at different time have the same position, here we just check if
|
|
||||||
// there are multiple slices for the same ImagePositionPatient and disable MPR.
|
|
||||||
|
|
||||||
// A better heuristic would be checking 4D tags, e.g. the presence of multiple TemporalPositionIdentifier values.
|
|
||||||
// However, some studies (e.g. https://github.com/OHIF/Viewers/issues/2113) do not have such tags.
|
|
||||||
|
|
||||||
for (let ii = 0; ii < displaySet.numImageFrames; ++ii) {
|
|
||||||
const image = displaySet.images[ii];
|
|
||||||
if (!image) continue;
|
|
||||||
|
|
||||||
const imageIdControl = image.getImageId()
|
|
||||||
const instanceMetadataControl = cornerstone.metaData.get('instance', imageIdControl)
|
|
||||||
|
|
||||||
if (!instanceMetadataControl ||
|
|
||||||
instanceMetadataControl === undefined ||
|
|
||||||
!instanceMetadataControl.ImagePositionPatient ||
|
|
||||||
instanceMetadataControl.ImagePositionPatient === undefined) {
|
|
||||||
// if ImagePositionPatient is missing, skip the 4D datasets check.
|
|
||||||
// do not return false, because it could be a 3D dataset.
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
let xImagePositionPatientControl = instanceMetadataControl.ImagePositionPatient[0];
|
|
||||||
let yImagePositionPatientControl = instanceMetadataControl.ImagePositionPatient[1];
|
|
||||||
let zImagePositionPatientControl = instanceMetadataControl.ImagePositionPatient[2];
|
|
||||||
|
|
||||||
for (let jj = ii + 1; jj < displaySet.numImageFrames; ++jj) {
|
|
||||||
const image = displaySet.images[jj];
|
|
||||||
if (!image) continue;
|
|
||||||
|
|
||||||
const imageId = image.getImageId()
|
|
||||||
const instanceMetadata = cornerstone.metaData.get('instance', imageId)
|
|
||||||
|
|
||||||
if (!instanceMetadata ||
|
|
||||||
instanceMetadata === undefined ||
|
|
||||||
!instanceMetadata.ImagePositionPatient ||
|
|
||||||
instanceMetadata.ImagePositionPatient === undefined) {
|
|
||||||
// if ImagePositionPatient is missing, skip the 4D datasets check.
|
|
||||||
// do not return false, because it could be a 3D dataset.
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
let xImagePositionPatient = instanceMetadata.ImagePositionPatient[0];
|
|
||||||
let yImagePositionPatient = instanceMetadata.ImagePositionPatient[1];
|
|
||||||
let zImagePositionPatient = instanceMetadata.ImagePositionPatient[2];
|
|
||||||
|
|
||||||
if (xImagePositionPatientControl === xImagePositionPatient &&
|
|
||||||
yImagePositionPatientControl === yImagePositionPatient &&
|
|
||||||
zImagePositionPatientControl === zImagePositionPatient) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return displaySet.isReconstructable;
|
return displaySet.isReconstructable;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -3,6 +3,181 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
## [2.13.15](https://github.com/OHIF/Viewers/compare/@ohif/core@2.13.14...@ohif/core@2.13.15) (2021-09-03)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/core
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [2.13.14](https://github.com/OHIF/Viewers/compare/@ohif/core@2.13.13...@ohif/core@2.13.14) (2021-08-30)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* MPR metadata parsing for spacings ([#2517](https://github.com/OHIF/Viewers/issues/2517)) ([715dfc5](https://github.com/OHIF/Viewers/commit/715dfc556a3883de31d74012dcc99e431d91f57d))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [2.13.13](https://github.com/OHIF/Viewers/compare/@ohif/core@2.13.12...@ohif/core@2.13.13) (2021-06-03)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/core
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [2.13.12](https://github.com/OHIF/Viewers/compare/@ohif/core@2.13.11...@ohif/core@2.13.12) (2021-06-02)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **dicom-html:** Add parsed dicom meta info section ([#2419](https://github.com/OHIF/Viewers/issues/2419)) ([403688b](https://github.com/OHIF/Viewers/commit/403688b18c52468fc1101166ec0c1734fb710039))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [2.13.11](https://github.com/OHIF/Viewers/compare/@ohif/core@2.13.10...@ohif/core@2.13.11) (2021-05-17)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/core
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [2.13.10](https://github.com/OHIF/Viewers/compare/@ohif/core@2.13.9...@ohif/core@2.13.10) (2021-04-22)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/core
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [2.13.9](https://github.com/OHIF/Viewers/compare/@ohif/core@2.13.8...@ohif/core@2.13.9) (2021-04-22)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/core
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [2.13.8](https://github.com/OHIF/Viewers/compare/@ohif/core@2.13.7...@ohif/core@2.13.8) (2021-04-21)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/core
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [2.13.7](https://github.com/OHIF/Viewers/compare/@ohif/core@2.13.6...@ohif/core@2.13.7) (2021-04-21)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/core
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [2.13.6](https://github.com/OHIF/Viewers/compare/@ohif/core@2.13.5...@ohif/core@2.13.6) (2021-04-21)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/core
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [2.13.5](https://github.com/OHIF/Viewers/compare/@ohif/core@2.13.4...@ohif/core@2.13.5) (2021-04-16)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/core
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [2.13.4](https://github.com/OHIF/Viewers/compare/@ohif/core@2.13.3...@ohif/core@2.13.4) (2021-04-15)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **wl:** fix metadata provider window level issues after dicomweb update (string vs number) ([8681036](https://github.com/OHIF/Viewers/commit/8681036b2133cfc325658691a256a86b5e36705f))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [2.13.3](https://github.com/OHIF/Viewers/compare/@ohif/core@2.13.2...@ohif/core@2.13.3) (2021-03-26)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/core
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [2.13.2](https://github.com/OHIF/Viewers/compare/@ohif/core@2.13.1...@ohif/core@2.13.2) (2021-03-25)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* IDC-2288: Update toolbar tabs after derived displayset loaded ([#2307](https://github.com/OHIF/Viewers/issues/2307)) ([6e723eb](https://github.com/OHIF/Viewers/commit/6e723ebced58852007ca553792c740976cfc4201))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [2.13.1](https://github.com/OHIF/Viewers/compare/@ohif/core@2.13.0...@ohif/core@2.13.1) (2021-03-09)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/core
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# [2.13.0](https://github.com/OHIF/Viewers/compare/@ohif/core@2.12.5...@ohif/core@2.13.0) (2021-03-04)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **log:** add new log service ([14d6454](https://github.com/OHIF/Viewers/commit/14d6454eafaa2ccb50e133c2945c9558052ea27e))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [2.12.5](https://github.com/OHIF/Viewers/compare/@ohif/core@2.12.4...@ohif/core@2.12.5) (2021-03-03)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/core
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [2.12.4](https://github.com/OHIF/Viewers/compare/@ohif/core@2.12.3...@ohif/core@2.12.4) (2021-02-25)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/core
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [2.12.3](https://github.com/OHIF/Viewers/compare/@ohif/core@2.12.2...@ohif/core@2.12.3) (2021-02-05)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/core
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [2.12.2](https://github.com/OHIF/Viewers/compare/@ohif/core@2.12.1...@ohif/core@2.12.2) (2021-02-05)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/core
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## [2.12.1](https://github.com/OHIF/Viewers/compare/@ohif/core@2.12.0...@ohif/core@2.12.1) (2021-01-21)
|
## [2.12.1](https://github.com/OHIF/Viewers/compare/@ohif/core@2.12.0...@ohif/core@2.12.1) (2021-01-21)
|
||||||
|
|
||||||
**Note:** Version bump only for package @ohif/core
|
**Note:** Version bump only for package @ohif/core
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@ohif/core",
|
"name": "@ohif/core",
|
||||||
"version": "2.12.1",
|
"version": "2.13.15",
|
||||||
"description": "Generic business logic for web-based medical imaging applications",
|
"description": "Generic business logic for web-based medical imaging applications",
|
||||||
"author": "OHIF Core Team",
|
"author": "OHIF Core Team",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
@@ -39,13 +39,14 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "^7.5.5",
|
"@babel/runtime": "^7.5.5",
|
||||||
"ajv": "^6.10.0",
|
"ajv": "^6.10.0",
|
||||||
"dcmjs": "0.17.0",
|
"dcmjs": "0.18.8",
|
||||||
"dicomweb-client": "^0.6.0",
|
"dicomweb-client": "^0.8.1",
|
||||||
"immer": "6.0.2",
|
"immer": "6.0.2",
|
||||||
"isomorphic-base64": "^1.0.2",
|
"isomorphic-base64": "^1.0.2",
|
||||||
"lodash.clonedeep": "^4.5.0",
|
"lodash.clonedeep": "^4.5.0",
|
||||||
"lodash.merge": "^4.6.1",
|
"lodash.merge": "^4.6.1",
|
||||||
"mousetrap": "^1.6.3",
|
"mousetrap": "^1.6.3",
|
||||||
|
"retry": "^0.12.0",
|
||||||
"validate.js": "^0.12.0"
|
"validate.js": "^0.12.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -6,6 +6,7 @@ import parseDicomStructuredReport from './parseDicomStructuredReport';
|
|||||||
import parseMeasurementsData from './parseMeasurementsData';
|
import parseMeasurementsData from './parseMeasurementsData';
|
||||||
import getAllDisplaySets from './utils/getAllDisplaySets';
|
import getAllDisplaySets from './utils/getAllDisplaySets';
|
||||||
import errorHandler from '../errorHandler';
|
import errorHandler from '../errorHandler';
|
||||||
|
import getXHRRetryRequestHook from '../utils/xhrRetryRequestHook';
|
||||||
|
|
||||||
const VERSION_NAME = 'dcmjs-0.0';
|
const VERSION_NAME = 'dcmjs-0.0';
|
||||||
const TRANSFER_SYNTAX_UID = '1.2.840.10008.1.2.1';
|
const TRANSFER_SYNTAX_UID = '1.2.840.10008.1.2.1';
|
||||||
@@ -23,6 +24,7 @@ const retrieveMeasurementFromSR = async (series, studies, serverUrl) => {
|
|||||||
url: serverUrl,
|
url: serverUrl,
|
||||||
headers: DICOMWeb.getAuthorizationHeader(),
|
headers: DICOMWeb.getAuthorizationHeader(),
|
||||||
errorInterceptor: errorHandler.getHTTPErrorHandler(),
|
errorInterceptor: errorHandler.getHTTPErrorHandler(),
|
||||||
|
requestHooks: [getXHRRetryRequestHook()],
|
||||||
};
|
};
|
||||||
|
|
||||||
const dicomWeb = new api.DICOMwebClient(config);
|
const dicomWeb = new api.DICOMwebClient(config);
|
||||||
@@ -74,6 +76,7 @@ const stowSRFromMeasurements = async (measurements, serverUrl) => {
|
|||||||
url: serverUrl,
|
url: serverUrl,
|
||||||
headers: DICOMWeb.getAuthorizationHeader(),
|
headers: DICOMWeb.getAuthorizationHeader(),
|
||||||
errorInterceptor: errorHandler.getHTTPErrorHandler(),
|
errorInterceptor: errorHandler.getHTTPErrorHandler(),
|
||||||
|
requestHooks: [getXHRRetryRequestHook()],
|
||||||
};
|
};
|
||||||
|
|
||||||
const dicomWeb = new api.DICOMwebClient(config);
|
const dicomWeb = new api.DICOMwebClient(config);
|
||||||
|
|||||||
@@ -77,7 +77,6 @@ const parseDicomStructuredReport = (part10SRArrayBuffer, displaySets) => {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Function to create imagePath with all imageData related
|
* Function to create imagePath with all imageData related
|
||||||
*
|
|
||||||
* @param {string} StudyInstanceUID
|
* @param {string} StudyInstanceUID
|
||||||
* @param {string} SeriesInstanceUID
|
* @param {string} SeriesInstanceUID
|
||||||
* @param {string} SOPInstanceUID
|
* @param {string} SOPInstanceUID
|
||||||
|
|||||||
@@ -63,10 +63,15 @@ export class HotkeysManager {
|
|||||||
|
|
||||||
definitions.forEach(definition => this.registerHotkeys(definition));
|
definitions.forEach(definition => this.registerHotkeys(definition));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const { UINotificationService } = this._servicesManager.services;
|
const {
|
||||||
|
UINotificationService,
|
||||||
|
LoggerService,
|
||||||
|
} = this._servicesManager.services;
|
||||||
|
const message = 'Erro while setting hotkeys';
|
||||||
|
LoggerService.error({ error, message });
|
||||||
UINotificationService.show({
|
UINotificationService.show({
|
||||||
title: 'Hotkeys Manager',
|
title: 'Hotkeys Manager',
|
||||||
message: 'Erro while setting hotkeys',
|
message,
|
||||||
type: 'error',
|
type: 'error',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import dicomParser from 'dicom-parser';
|
|||||||
import getPixelSpacingInformation from '../utils/metadataProvider/getPixelSpacingInformation';
|
import getPixelSpacingInformation from '../utils/metadataProvider/getPixelSpacingInformation';
|
||||||
import fetchPaletteColorLookupTableData from '../utils/metadataProvider/fetchPaletteColorLookupTableData';
|
import fetchPaletteColorLookupTableData from '../utils/metadataProvider/fetchPaletteColorLookupTableData';
|
||||||
import fetchOverlayData from '../utils/metadataProvider/fetchOverlayData';
|
import fetchOverlayData from '../utils/metadataProvider/fetchOverlayData';
|
||||||
|
import validNumber from '../utils/metadataProvider/validNumber';
|
||||||
|
|
||||||
class MetadataProvider {
|
class MetadataProvider {
|
||||||
constructor() {
|
constructor() {
|
||||||
@@ -230,26 +231,26 @@ class MetadataProvider {
|
|||||||
let columnCosines;
|
let columnCosines;
|
||||||
|
|
||||||
if (PixelSpacing) {
|
if (PixelSpacing) {
|
||||||
rowPixelSpacing = PixelSpacing[0];
|
rowPixelSpacing = validNumber(PixelSpacing[0]);
|
||||||
columnPixelSpacing = PixelSpacing[1];
|
columnPixelSpacing = validNumber(PixelSpacing[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ImageOrientationPatient) {
|
if (ImageOrientationPatient) {
|
||||||
rowCosines = ImageOrientationPatient.slice(0, 3);
|
rowCosines = validNumber(ImageOrientationPatient.slice(0, 3));
|
||||||
columnCosines = ImageOrientationPatient.slice(3, 6);
|
columnCosines = validNumber(ImageOrientationPatient.slice(3, 6));
|
||||||
}
|
}
|
||||||
|
|
||||||
metadata = {
|
metadata = {
|
||||||
frameOfReferenceUID: instance.FrameOfReferenceUID,
|
frameOfReferenceUID: instance.FrameOfReferenceUID,
|
||||||
rows: instance.Rows,
|
rows: instance.Rows,
|
||||||
columns: instance.Columns,
|
columns: instance.Columns,
|
||||||
imageOrientationPatient: ImageOrientationPatient,
|
imageOrientationPatient: validNumber(ImageOrientationPatient),
|
||||||
rowCosines,
|
rowCosines,
|
||||||
columnCosines,
|
columnCosines,
|
||||||
imagePositionPatient: instance.ImagePositionPatient,
|
imagePositionPatient: validNumber(instance.ImagePositionPatient),
|
||||||
sliceThickness: instance.SliceThickness,
|
sliceThickness: validNumber(instance.SliceThickness),
|
||||||
sliceLocation: instance.SliceLocation,
|
sliceLocation: validNumber(instance.SliceLocation),
|
||||||
pixelSpacing: PixelSpacing,
|
pixelSpacing: validNumber(PixelSpacing),
|
||||||
rowPixelSpacing,
|
rowPixelSpacing,
|
||||||
columnPixelSpacing,
|
columnPixelSpacing,
|
||||||
};
|
};
|
||||||
@@ -284,7 +285,7 @@ class MetadataProvider {
|
|||||||
|
|
||||||
break;
|
break;
|
||||||
case WADO_IMAGE_LOADER_TAGS.VOI_LUT_MODULE:
|
case WADO_IMAGE_LOADER_TAGS.VOI_LUT_MODULE:
|
||||||
const { WindowCenter, WindowWidth } = instance;
|
let { WindowCenter, WindowWidth } = instance;
|
||||||
|
|
||||||
const windowCenter = Array.isArray(WindowCenter)
|
const windowCenter = Array.isArray(WindowCenter)
|
||||||
? WindowCenter
|
? WindowCenter
|
||||||
@@ -294,15 +295,17 @@ class MetadataProvider {
|
|||||||
: [WindowWidth];
|
: [WindowWidth];
|
||||||
|
|
||||||
metadata = {
|
metadata = {
|
||||||
windowCenter,
|
windowCenter: validNumber(windowCenter),
|
||||||
windowWidth,
|
windowWidth: validNumber(windowWidth),
|
||||||
};
|
};
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case WADO_IMAGE_LOADER_TAGS.MODALITY_LUT_MODULE:
|
case WADO_IMAGE_LOADER_TAGS.MODALITY_LUT_MODULE:
|
||||||
|
const rescaleSlope = validNumber(instance.RescaleSlope);
|
||||||
|
const rescaleIntercept = validNumber(instance.RescaleIntercept);
|
||||||
metadata = {
|
metadata = {
|
||||||
rescaleIntercept: instance.RescaleIntercept,
|
rescaleIntercept,
|
||||||
rescaleSlope: instance.RescaleSlope,
|
rescaleSlope,
|
||||||
rescaleType: instance.RescaleType,
|
rescaleType: instance.RescaleType,
|
||||||
};
|
};
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -9,11 +9,12 @@ import { SeriesMetadata } from './SeriesMetadata';
|
|||||||
import { api } from 'dicomweb-client';
|
import { api } from 'dicomweb-client';
|
||||||
// - createStacks
|
// - createStacks
|
||||||
import { isImage } from '../../utils/isImage';
|
import { isImage } from '../../utils/isImage';
|
||||||
import isDisplaySetReconstructable from '../../utils/isDisplaySetReconstructable';
|
import { isDisplaySetReconstructable, isSpacingUniform } from '../../utils/isDisplaySetReconstructable';
|
||||||
import errorHandler from '../../errorHandler';
|
import errorHandler from '../../errorHandler';
|
||||||
import isLowPriorityModality from '../../utils/isLowPriorityModality';
|
import isLowPriorityModality from '../../utils/isLowPriorityModality';
|
||||||
|
import getXHRRetryRequestHook from '../../utils/xhrRetryRequestHook';
|
||||||
|
|
||||||
export class StudyMetadata extends Metadata {
|
class StudyMetadata extends Metadata {
|
||||||
constructor(data, uid) {
|
constructor(data, uid) {
|
||||||
super(data, uid);
|
super(data, uid);
|
||||||
// Initialize Private Properties
|
// Initialize Private Properties
|
||||||
@@ -226,6 +227,56 @@ export class StudyMetadata extends Metadata {
|
|||||||
displaySets.map(displaySet => this._derivedDisplaySets.push(displaySet));
|
displaySets.map(displaySet => this._derivedDisplaySets.push(displaySet));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the source display set of the derivated display set.
|
||||||
|
* @param {object} derivatedDisplaySet
|
||||||
|
* @param {array[StudyMetadata]} studies
|
||||||
|
* @return {object} source display set.
|
||||||
|
*/
|
||||||
|
static getReferencedDisplaySet(derivatedDisplaySet, studies) {
|
||||||
|
let allDisplaySets = [];
|
||||||
|
|
||||||
|
studies.forEach(study => {
|
||||||
|
allDisplaySets = allDisplaySets.concat(study.displaySets);
|
||||||
|
});
|
||||||
|
|
||||||
|
const otherDisplaySets = allDisplaySets.filter(
|
||||||
|
ds => ds.displaySetInstanceUID !== derivatedDisplaySet.displaySetInstanceUID
|
||||||
|
);
|
||||||
|
|
||||||
|
const { metadata } = derivatedDisplaySet;
|
||||||
|
|
||||||
|
let referencedSeriesInstanceUIDs = _findReferencedSeriesInstanceUIDsFromSourceImageSequence
|
||||||
|
(metadata, otherDisplaySets);
|
||||||
|
|
||||||
|
let noReferencedSeriesAvailable = !referencedSeriesInstanceUIDs ||
|
||||||
|
referencedSeriesInstanceUIDs.length === 0;
|
||||||
|
if (noReferencedSeriesAvailable) {
|
||||||
|
referencedSeriesInstanceUIDs =
|
||||||
|
_findReferencedSeriesInstanceUIDsFromReferencedSeriesSequence
|
||||||
|
(metadata);
|
||||||
|
}
|
||||||
|
|
||||||
|
noReferencedSeriesAvailable = !referencedSeriesInstanceUIDs ||
|
||||||
|
referencedSeriesInstanceUIDs.length === 0;
|
||||||
|
if (noReferencedSeriesAvailable) {
|
||||||
|
referencedSeriesInstanceUIDs =
|
||||||
|
_findReferencedSeriesInstanceUIDsFromReferencedImageSequence
|
||||||
|
(metadata, otherDisplaySets);
|
||||||
|
}
|
||||||
|
|
||||||
|
const referencedSeriesAvailable = referencedSeriesInstanceUIDs &&
|
||||||
|
referencedSeriesInstanceUIDs.length !== 0;
|
||||||
|
if (referencedSeriesAvailable) {
|
||||||
|
const referencedDisplaySet = otherDisplaySets.find(ds =>
|
||||||
|
referencedSeriesInstanceUIDs.includes(ds.SeriesInstanceUID)
|
||||||
|
);
|
||||||
|
;
|
||||||
|
return referencedDisplaySet;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a list of derived datasets in the study, filtered by the given filter.
|
* Returns a list of derived datasets in the study, filtered by the given filter.
|
||||||
* @param {object} filter An object containing search filters
|
* @param {object} filter An object containing search filters
|
||||||
@@ -252,20 +303,7 @@ export class StudyMetadata extends Metadata {
|
|||||||
if (referencedSeriesInstanceUID) {
|
if (referencedSeriesInstanceUID) {
|
||||||
filteredDerivedDisplaySets = filteredDerivedDisplaySets.filter(
|
filteredDerivedDisplaySets = filteredDerivedDisplaySets.filter(
|
||||||
displaySet => {
|
displaySet => {
|
||||||
if (!displaySet.metadata.ReferencedSeriesSequence) {
|
return StudyMetadata.getReferencedDisplaySet(displaySet, [this]).SeriesInstanceUID === referencedSeriesInstanceUID;
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const ReferencedSeriesSequence = Array.isArray(
|
|
||||||
displaySet.metadata.ReferencedSeriesSequence
|
|
||||||
)
|
|
||||||
? displaySet.metadata.ReferencedSeriesSequence
|
|
||||||
: [displaySet.metadata.ReferencedSeriesSequence];
|
|
||||||
|
|
||||||
return ReferencedSeriesSequence.some(
|
|
||||||
ReferencedSeries =>
|
|
||||||
ReferencedSeries.SeriesInstanceUID === referencedSeriesInstanceUID
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -273,7 +311,7 @@ export class StudyMetadata extends Metadata {
|
|||||||
if (referencedFrameOfReferenceUID) {
|
if (referencedFrameOfReferenceUID) {
|
||||||
filteredDerivedDisplaySets = filteredDerivedDisplaySets.filter(
|
filteredDerivedDisplaySets = filteredDerivedDisplaySets.filter(
|
||||||
displaySet =>
|
displaySet =>
|
||||||
displaySet.ReferencedFrameOfReferenceUID ===
|
displaySet.FrameOfReferenceUID ===
|
||||||
referencedFrameOfReferenceUID
|
referencedFrameOfReferenceUID
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -767,6 +805,16 @@ const isMultiFrame = instance => {
|
|||||||
return instance.getTagValue('NumberOfFrames') > 1;
|
return instance.getTagValue('NumberOfFrames') > 1;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a display set for a series.
|
||||||
|
* Checks if a series is reconstructable to a 3D volume.
|
||||||
|
* If reconstructable, the frames are sorted.
|
||||||
|
*
|
||||||
|
* @param {SeriesMetadata} series The series metadata object from which the display sets will be created
|
||||||
|
* @param {Object[]} instances An array of `OHIFInstanceMetadata` objects.
|
||||||
|
*
|
||||||
|
* @returns {Object} imageSet.
|
||||||
|
*/
|
||||||
const makeDisplaySet = (series, instances) => {
|
const makeDisplaySet = (series, instances) => {
|
||||||
const instance = instances[0];
|
const instance = instances[0];
|
||||||
const imageSet = new ImageSet(instances);
|
const imageSet = new ImageSet(instances);
|
||||||
@@ -786,7 +834,7 @@ const makeDisplaySet = (series, instances) => {
|
|||||||
isMultiFrame: isMultiFrame(instance),
|
isMultiFrame: isMultiFrame(instance),
|
||||||
});
|
});
|
||||||
|
|
||||||
// Sort the images in this series if needed
|
// Sort the images in this series by instanceNumber
|
||||||
const shallSort = true; //!OHIF.utils.ObjectPath.get(Meteor, 'settings.public.ui.sortSeriesByIncomingOrder');
|
const shallSort = true; //!OHIF.utils.ObjectPath.get(Meteor, 'settings.public.ui.sortSeriesByIncomingOrder');
|
||||||
if (shallSort) {
|
if (shallSort) {
|
||||||
imageSet.sortBy((a, b) => {
|
imageSet.sortBy((a, b) => {
|
||||||
@@ -804,18 +852,32 @@ const makeDisplaySet = (series, instances) => {
|
|||||||
imageSet.getImage(0).getTagValue('InstanceNumber')
|
imageSet.getImage(0).getTagValue('InstanceNumber')
|
||||||
);
|
);
|
||||||
|
|
||||||
const isReconstructable = isDisplaySetReconstructable(instances);
|
const displayReconstructableInfo = isDisplaySetReconstructable(instances);
|
||||||
|
imageSet.isReconstructable = displayReconstructableInfo.value;
|
||||||
imageSet.isReconstructable = isReconstructable.value;
|
|
||||||
|
|
||||||
|
let displaySpacingInfo = undefined;
|
||||||
if (shallSort && imageSet.isReconstructable) {
|
if (shallSort && imageSet.isReconstructable) {
|
||||||
|
// sort images by image position
|
||||||
imageSet.sortByImagePositionPatient();
|
imageSet.sortByImagePositionPatient();
|
||||||
|
|
||||||
|
// check if the spacing is uniform and update isReconstructable
|
||||||
|
const datasetIs4D = displayReconstructableInfo.reconstructionIssues.find
|
||||||
|
(issue => issue === ReconstructionIssues.DATASET_4D);
|
||||||
|
displaySpacingInfo = isSpacingUniform(imageSet.images, datasetIs4D);
|
||||||
|
imageSet.isReconstructable = displaySpacingInfo.isUniform;
|
||||||
|
|
||||||
|
if (displaySpacingInfo.missingFrames) {
|
||||||
|
// TODO -> This is currently unused, but may be used for reconstructing
|
||||||
|
// Volumes with gaps later on.
|
||||||
|
imageSet.missingFrames = displaySpacingInfo.missingFrames;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isReconstructable.missingFrames) {
|
if (!imageSet.displayReconstructableInfo) {
|
||||||
// TODO -> This is currently unused, but may be used for reconstructing
|
// It is not reconstrabale Save type of warning
|
||||||
// Volumes with gaps later on.
|
imageSet.reconstructionIssues = displaySpacingInfo ?
|
||||||
imageSet.missingFrames = isReconstructable.missingFrames;
|
displayReconstructableInfo.reconstructionIssues.concat(displaySpacingInfo.reconstructionIssues) :
|
||||||
|
displayReconstructableInfo.reconstructionIssues;
|
||||||
}
|
}
|
||||||
|
|
||||||
return imageSet;
|
return imageSet;
|
||||||
@@ -878,6 +940,7 @@ function _getDisplaySetFromSopClassModule(
|
|||||||
url: study.getData().wadoRoot,
|
url: study.getData().wadoRoot,
|
||||||
headers,
|
headers,
|
||||||
errorInterceptor,
|
errorInterceptor,
|
||||||
|
requestHooks: [getXHRRetryRequestHook()],
|
||||||
});
|
});
|
||||||
|
|
||||||
let displaySet = plugin.getDisplaySetFromSeries(
|
let displaySet = plugin.getDisplaySetFromSeries(
|
||||||
@@ -892,3 +955,147 @@ function _getDisplaySetFromSopClassModule(
|
|||||||
}
|
}
|
||||||
return displaySet;
|
return displaySet;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the referenced series instance UIDs by searching the information in the
|
||||||
|
* ReferencedSeriesSequence.
|
||||||
|
* @param {object} derivatedDisplaySet.metadata
|
||||||
|
* @return {array[string]} referenced series instance UIDs.
|
||||||
|
*/
|
||||||
|
function _findReferencedSeriesInstanceUIDsFromReferencedSeriesSequence (
|
||||||
|
metadata,
|
||||||
|
) {
|
||||||
|
if (!metadata.ReferencedSeriesSequence) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let referencedSeriesInstanceUIDs;
|
||||||
|
const ReferencedSeriesSequence = _toArray(
|
||||||
|
metadata.ReferencedSeriesSequence
|
||||||
|
);
|
||||||
|
|
||||||
|
referencedSeriesInstanceUIDs = ReferencedSeriesSequence.map(
|
||||||
|
ReferencedSeries => ReferencedSeries.SeriesInstanceUID
|
||||||
|
);
|
||||||
|
|
||||||
|
return referencedSeriesInstanceUIDs;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the referenced series instance UIDs by searching the information in the
|
||||||
|
* ReferencedImageSequence.
|
||||||
|
* @param {object} derivatedDisplaySet.metadata
|
||||||
|
* @param {array[object]} displaysets
|
||||||
|
* @return {array[string]} referenced series instance UIDs.
|
||||||
|
*/
|
||||||
|
function _findReferencedSeriesInstanceUIDsFromReferencedImageSequence (
|
||||||
|
metadata,
|
||||||
|
displaySets
|
||||||
|
) {
|
||||||
|
if (!metadata.ReferencedImageSequence) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let referencedSeriesInstanceUIDs;
|
||||||
|
const referencedImageArray = _toArray(metadata.ReferencedImageSequence);
|
||||||
|
for (let i = 0; i < referencedImageArray.length; i++) {
|
||||||
|
const { ReferencedSOPInstanceUID } = referencedImageArray[i];
|
||||||
|
if (!ReferencedSOPInstanceUID) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
referencedSeriesInstanceUIDs = _findReferencedSeriesInstanceUIDsFromSOPInstanceUID(
|
||||||
|
displaySets,
|
||||||
|
ReferencedSOPInstanceUID
|
||||||
|
);
|
||||||
|
|
||||||
|
if (referencedSeriesInstanceUIDs && referencedSeriesInstanceUIDs.length !== 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return referencedSeriesInstanceUIDs;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the referenced series instance UIDs by searching the information in the
|
||||||
|
* SourceImageSequence.
|
||||||
|
* @param {object} derivatedDisplaySet.metadata
|
||||||
|
* @param {array[object]} displaysets
|
||||||
|
* @return {array[string]} referenced series instance UIDs.
|
||||||
|
*/
|
||||||
|
function _findReferencedSeriesInstanceUIDsFromSourceImageSequence (
|
||||||
|
metadata,
|
||||||
|
displaySets
|
||||||
|
) {
|
||||||
|
let SourceImageSequence;
|
||||||
|
|
||||||
|
if (metadata.SourceImageSequence) {
|
||||||
|
SourceImageSequence = metadata.SourceImageSequence;
|
||||||
|
} else {
|
||||||
|
const { PerFrameFunctionalGroupsSequence } = metadata;
|
||||||
|
const firstFunctionalGroups = _toArray(
|
||||||
|
PerFrameFunctionalGroupsSequence
|
||||||
|
)[0];
|
||||||
|
if (firstFunctionalGroups) {
|
||||||
|
const { DerivationImageSequence } = firstFunctionalGroups;
|
||||||
|
SourceImageSequence = DerivationImageSequence;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!SourceImageSequence) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const sourceImageArray = _toArray(SourceImageSequence);
|
||||||
|
|
||||||
|
let referencedSeriesInstanceUIDs;
|
||||||
|
for (let i = 0; i < sourceImageArray.length; i++) {
|
||||||
|
const { ReferencedSOPInstanceUID } = sourceImageArray[i];
|
||||||
|
referencedSeriesInstanceUIDs = _findReferencedSeriesInstanceUIDsFromSOPInstanceUID(
|
||||||
|
displaySets,
|
||||||
|
ReferencedSOPInstanceUID
|
||||||
|
);
|
||||||
|
if (referencedSeriesInstanceUIDs && referencedSeriesInstanceUIDs.length !== 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return referencedSeriesInstanceUIDs;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the referenced series instance UIDs by searching the information in the
|
||||||
|
* SOPInstanceUID of the displaySets.
|
||||||
|
* @param {array[object]} displaysets
|
||||||
|
* @param {string} SOPInstanceUID
|
||||||
|
* @return {array[string]} referenced series instance UIDs.
|
||||||
|
*/
|
||||||
|
function _findReferencedSeriesInstanceUIDsFromSOPInstanceUID (
|
||||||
|
displaySets,
|
||||||
|
SOPInstanceUID
|
||||||
|
) {
|
||||||
|
const imageSets = displaySets.filter(ds => ds instanceof ImageSet);
|
||||||
|
|
||||||
|
for (let i = 0; i < imageSets.length; i++) {
|
||||||
|
const { images } = imageSets[i];
|
||||||
|
if (!images) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
for (let j = 0; j < images.length; j++) {
|
||||||
|
const image = images[j];
|
||||||
|
if (!image) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (image.SOPInstanceUID === SOPInstanceUID) {
|
||||||
|
return [image.getData().metadata.SeriesInstanceUID];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
function _toArray(arrayOrObject) {
|
||||||
|
return Array.isArray(arrayOrObject) ? arrayOrObject : [arrayOrObject];
|
||||||
|
}
|
||||||
|
|
||||||
|
export {StudyMetadata};
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
const ReconstructionIssues = {
|
||||||
|
DATASET_4D: 'datasetis4D',
|
||||||
|
VARYING_IMAGESDIMENSIONS: 'imagesdimensionsvarying',
|
||||||
|
VARYING_IMAGESCOMPONENTS: 'imagescomponentsvarying',
|
||||||
|
VARYING_IMAGESORIENTATION: 'imagesorientationvarying',
|
||||||
|
MISSING_FRAMES: 'missingframes',
|
||||||
|
IRREGULAR_SPACING: 'irregularspacing',
|
||||||
|
MULTIFFRAMES: 'multiframe',
|
||||||
|
};
|
||||||
|
|
||||||
|
export {ReconstructionIssues};
|
||||||
@@ -26,6 +26,7 @@ import {
|
|||||||
UIModalService,
|
UIModalService,
|
||||||
UIDialogService,
|
UIDialogService,
|
||||||
MeasurementService,
|
MeasurementService,
|
||||||
|
LoggerService,
|
||||||
} from './services';
|
} from './services';
|
||||||
|
|
||||||
const OHIF = {
|
const OHIF = {
|
||||||
@@ -60,6 +61,7 @@ const OHIF = {
|
|||||||
UIModalService,
|
UIModalService,
|
||||||
UIDialogService,
|
UIDialogService,
|
||||||
MeasurementService,
|
MeasurementService,
|
||||||
|
LoggerService,
|
||||||
};
|
};
|
||||||
|
|
||||||
export {
|
export {
|
||||||
@@ -93,6 +95,7 @@ export {
|
|||||||
UIModalService,
|
UIModalService,
|
||||||
UIDialogService,
|
UIDialogService,
|
||||||
MeasurementService,
|
MeasurementService,
|
||||||
|
LoggerService,
|
||||||
};
|
};
|
||||||
|
|
||||||
export { OHIF };
|
export { OHIF };
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ describe('Top level exports', () => {
|
|||||||
'UIModalService',
|
'UIModalService',
|
||||||
'UIDialogService',
|
'UIDialogService',
|
||||||
'MeasurementService',
|
'MeasurementService',
|
||||||
|
'LoggerService',
|
||||||
//
|
//
|
||||||
'utils',
|
'utils',
|
||||||
'hotkeys',
|
'hotkeys',
|
||||||
|
|||||||
@@ -0,0 +1,67 @@
|
|||||||
|
const name = 'LoggerService';
|
||||||
|
|
||||||
|
const publicAPI = {
|
||||||
|
name,
|
||||||
|
info: _info,
|
||||||
|
error: _error,
|
||||||
|
setServiceImplementation,
|
||||||
|
};
|
||||||
|
|
||||||
|
const serviceImplementation = {
|
||||||
|
_info: () => console.warn('info() NOT IMPLEMENTED'),
|
||||||
|
_error: () => console.warn('error() NOT IMPLEMENTED'),
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Logs an info
|
||||||
|
*
|
||||||
|
* @param {object} props { message, displayOnConsole }
|
||||||
|
*/
|
||||||
|
function _info({ message, displayOnConsole }) {
|
||||||
|
return serviceImplementation._info({
|
||||||
|
message,
|
||||||
|
displayOnConsole,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Logs an error
|
||||||
|
*
|
||||||
|
* @param {object} props { error, stack, message, displayOnConsole }
|
||||||
|
* @returns void
|
||||||
|
*/
|
||||||
|
function _error({ error, stack, message, displayOnConsole }) {
|
||||||
|
return serviceImplementation._error({
|
||||||
|
error,
|
||||||
|
stack,
|
||||||
|
message,
|
||||||
|
displayOnConsole,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @param {*} {
|
||||||
|
* info: infoImplementation,
|
||||||
|
* error: errorImplementation,
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
function setServiceImplementation({
|
||||||
|
info: infoImplementation,
|
||||||
|
error: errorImplementation,
|
||||||
|
}) {
|
||||||
|
if (infoImplementation) {
|
||||||
|
serviceImplementation._info = infoImplementation;
|
||||||
|
}
|
||||||
|
if (errorImplementation) {
|
||||||
|
serviceImplementation._error = errorImplementation;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name,
|
||||||
|
create: ({ configuration = {} }) => {
|
||||||
|
return publicAPI;
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -260,7 +260,6 @@ class MeasurementService {
|
|||||||
const sourceInfo = this._getSourceInfo(source);
|
const sourceInfo = this._getSourceInfo(source);
|
||||||
|
|
||||||
if (!definition) {
|
if (!definition) {
|
||||||
console.log('TEST');
|
|
||||||
log.warn('No source definition provided. Exiting early.');
|
log.warn('No source definition provided. Exiting early.');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,8 @@
|
|||||||
* @property {boolean} [closeButton=true] Should the modal body render the close button.
|
* @property {boolean} [closeButton=true] Should the modal body render the close button.
|
||||||
* @property {string} [title=null] Should the modal render the title independently of the body content.
|
* @property {string} [title=null] Should the modal render the title independently of the body content.
|
||||||
* @property {string} [customClassName=null] The custom class to style the modal.
|
* @property {string} [customClassName=null] The custom class to style the modal.
|
||||||
* @property {boolean} [showScrollbar=null] Show or hide scrollbar.
|
* @property {boolean} [showScrollbar=false] Show or hide scrollbar.
|
||||||
|
* @property {boolean} [noScroll=false] Disable or not the scrollbar.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const name = 'UIModalService';
|
const name = 'UIModalService';
|
||||||
@@ -29,7 +30,7 @@ const serviceImplementation = {
|
|||||||
/**
|
/**
|
||||||
* Show a new UI modal;
|
* Show a new UI modal;
|
||||||
*
|
*
|
||||||
* @param {ModalProps} props { content, contentProps, shouldCloseOnEsc, isOpen, onClose, closeButton, title, customClassName, showScrollbar }
|
* @param {ModalProps} props { content, contentProps, shouldCloseOnEsc, isOpen, onClose, closeButton, title, customClassName, showScrollbar, noScroll }
|
||||||
*/
|
*/
|
||||||
function _show({
|
function _show({
|
||||||
content = null,
|
content = null,
|
||||||
@@ -41,7 +42,8 @@ function _show({
|
|||||||
title = null,
|
title = null,
|
||||||
fullscreen = false,
|
fullscreen = false,
|
||||||
customClassName = null,
|
customClassName = null,
|
||||||
showScrollbar = false
|
showScrollbar = false,
|
||||||
|
noScroll = false,
|
||||||
}) {
|
}) {
|
||||||
return serviceImplementation._show({
|
return serviceImplementation._show({
|
||||||
content,
|
content,
|
||||||
@@ -53,7 +55,8 @@ function _show({
|
|||||||
title,
|
title,
|
||||||
fullscreen,
|
fullscreen,
|
||||||
customClassName,
|
customClassName,
|
||||||
showScrollbar
|
showScrollbar,
|
||||||
|
noScroll,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import UINotificationService from './UINotificationService';
|
|||||||
import UIModalService from './UIModalService';
|
import UIModalService from './UIModalService';
|
||||||
import UIDialogService from './UIDialogService';
|
import UIDialogService from './UIDialogService';
|
||||||
import MeasurementService from './MeasurementService';
|
import MeasurementService from './MeasurementService';
|
||||||
|
import LoggerService from './LoggerService';
|
||||||
|
|
||||||
export {
|
export {
|
||||||
UINotificationService,
|
UINotificationService,
|
||||||
@@ -10,4 +11,5 @@ export {
|
|||||||
UIDialogService,
|
UIDialogService,
|
||||||
ServicesManager,
|
ServicesManager,
|
||||||
MeasurementService,
|
MeasurementService,
|
||||||
|
LoggerService,
|
||||||
};
|
};
|
||||||
@@ -2,6 +2,7 @@ import { api } from 'dicomweb-client';
|
|||||||
import DICOMWeb from '../../../DICOMWeb/';
|
import DICOMWeb from '../../../DICOMWeb/';
|
||||||
|
|
||||||
import errorHandler from '../../../errorHandler';
|
import errorHandler from '../../../errorHandler';
|
||||||
|
import getXHRRetryRequestHook from '../../../utils/xhrRetryRequestHook';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a QIDO date string for a date range query
|
* Creates a QIDO date string for a date range query
|
||||||
@@ -118,6 +119,7 @@ export default function Studies(server, filter) {
|
|||||||
url: server.qidoRoot,
|
url: server.qidoRoot,
|
||||||
headers: DICOMWeb.getAuthorizationHeader(server),
|
headers: DICOMWeb.getAuthorizationHeader(server),
|
||||||
errorInterceptor: errorHandler.getHTTPErrorHandler(),
|
errorInterceptor: errorHandler.getHTTPErrorHandler(),
|
||||||
|
requestHooks: [getXHRRetryRequestHook()],
|
||||||
};
|
};
|
||||||
|
|
||||||
const dicomWeb = new api.DICOMwebClient(config);
|
const dicomWeb = new api.DICOMwebClient(config);
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import {
|
|||||||
} from './studyInstanceHelpers';
|
} from './studyInstanceHelpers';
|
||||||
|
|
||||||
import errorHandler from '../../../errorHandler';
|
import errorHandler from '../../../errorHandler';
|
||||||
|
import { getXHRRetryRequestHook } from '../../../utils/xhrRetryRequestHook';
|
||||||
|
|
||||||
const { naturalizeDataset } = dcmjs.data.DicomMetaDictionary;
|
const { naturalizeDataset } = dcmjs.data.DicomMetaDictionary;
|
||||||
|
|
||||||
@@ -76,6 +77,7 @@ export default class RetrieveMetadataLoaderAsync extends RetrieveMetadataLoader
|
|||||||
url: server.qidoRoot,
|
url: server.qidoRoot,
|
||||||
headers: DICOMWeb.getAuthorizationHeader(server),
|
headers: DICOMWeb.getAuthorizationHeader(server),
|
||||||
errorInterceptor: errorHandler.getHTTPErrorHandler(),
|
errorInterceptor: errorHandler.getHTTPErrorHandler(),
|
||||||
|
requestHooks: [getXHRRetryRequestHook()],
|
||||||
});
|
});
|
||||||
|
|
||||||
this.client = client;
|
this.client = client;
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { createStudyFromSOPInstanceList } from './studyInstanceHelpers';
|
|||||||
import RetrieveMetadataLoader from './retrieveMetadataLoader';
|
import RetrieveMetadataLoader from './retrieveMetadataLoader';
|
||||||
|
|
||||||
import errorHandler from '../../../errorHandler';
|
import errorHandler from '../../../errorHandler';
|
||||||
|
import getXHRRetryRequestHook from '../../../utils/xhrRetryRequestHook';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class for sync load of study metadata.
|
* Class for sync load of study metadata.
|
||||||
@@ -61,6 +62,7 @@ export default class RetrieveMetadataLoaderSync extends RetrieveMetadataLoader {
|
|||||||
url: server.wadoRoot,
|
url: server.wadoRoot,
|
||||||
headers: DICOMWeb.getAuthorizationHeader(server),
|
headers: DICOMWeb.getAuthorizationHeader(server),
|
||||||
errorInterceptor: errorHandler.getHTTPErrorHandler(),
|
errorInterceptor: errorHandler.getHTTPErrorHandler(),
|
||||||
|
requestHooks: [getXHRRetryRequestHook()],
|
||||||
});
|
});
|
||||||
|
|
||||||
this.client = client;
|
this.client = client;
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { api } from 'dicomweb-client';
|
|||||||
import DICOMWeb from '../DICOMWeb';
|
import DICOMWeb from '../DICOMWeb';
|
||||||
|
|
||||||
import errorHandler from '../errorHandler';
|
import errorHandler from '../errorHandler';
|
||||||
|
import getXHRRetryRequestHook from './xhrRetryRequestHook';
|
||||||
|
|
||||||
const getImageId = imageObj => {
|
const getImageId = imageObj => {
|
||||||
if (!imageObj) {
|
if (!imageObj) {
|
||||||
@@ -66,6 +67,7 @@ const wadorsRetriever = (
|
|||||||
url,
|
url,
|
||||||
headers,
|
headers,
|
||||||
errorInterceptor,
|
errorInterceptor,
|
||||||
|
requestHooks: [getXHRRetryRequestHook()],
|
||||||
};
|
};
|
||||||
const dicomWeb = new api.DICOMwebClient(config);
|
const dicomWeb = new api.DICOMwebClient(config);
|
||||||
|
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import isDicomUid from './isDicomUid';
|
|||||||
import resolveObjectPath from './resolveObjectPath';
|
import resolveObjectPath from './resolveObjectPath';
|
||||||
import * as hierarchicalListUtils from './hierarchicalListUtils';
|
import * as hierarchicalListUtils from './hierarchicalListUtils';
|
||||||
import * as progressTrackingUtils from './progressTrackingUtils';
|
import * as progressTrackingUtils from './progressTrackingUtils';
|
||||||
|
import xhrRetryRequestHook from './xhrRetryRequestHook';
|
||||||
|
|
||||||
const utils = {
|
const utils = {
|
||||||
guid,
|
guid,
|
||||||
@@ -40,6 +41,7 @@ const utils = {
|
|||||||
resolveObjectPath,
|
resolveObjectPath,
|
||||||
hierarchicalListUtils,
|
hierarchicalListUtils,
|
||||||
progressTrackingUtils,
|
progressTrackingUtils,
|
||||||
|
xhrRetryRequestHook,
|
||||||
};
|
};
|
||||||
|
|
||||||
export {
|
export {
|
||||||
@@ -63,6 +65,7 @@ export {
|
|||||||
resolveObjectPath,
|
resolveObjectPath,
|
||||||
hierarchicalListUtils,
|
hierarchicalListUtils,
|
||||||
progressTrackingUtils,
|
progressTrackingUtils,
|
||||||
|
xhrRetryRequestHook,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default utils;
|
export default utils;
|
||||||
@@ -23,6 +23,7 @@ describe('Top level exports', () => {
|
|||||||
'resolveObjectPath',
|
'resolveObjectPath',
|
||||||
'hierarchicalListUtils',
|
'hierarchicalListUtils',
|
||||||
'progressTrackingUtils',
|
'progressTrackingUtils',
|
||||||
|
'xhrRetryRequestHook',
|
||||||
].sort();
|
].sort();
|
||||||
|
|
||||||
const exports = Object.keys(utils.default).sort();
|
const exports = Object.keys(utils.default).sort();
|
||||||
|
|||||||
@@ -1,9 +1,14 @@
|
|||||||
|
import { ReconstructionIssues } from './../enums.js';
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if a series is reconstructable to a 3D volume.
|
* Checks if a series is reconstructable to a 3D volume.
|
||||||
*
|
*
|
||||||
* @param {Object[]} instances An array of `OHIFInstanceMetadata` objects.
|
* @param {Object[]} An array of `OHIFInstanceMetadata` objects.
|
||||||
|
*
|
||||||
|
* @returns {Object} value, reconstructionIssues.
|
||||||
*/
|
*/
|
||||||
export default function isDisplaySetReconstructable(instances) {
|
function isDisplaySetReconstructable(instances) {
|
||||||
if (!instances.length) {
|
if (!instances.length) {
|
||||||
return { value: false };
|
return { value: false };
|
||||||
}
|
}
|
||||||
@@ -23,31 +28,45 @@ export default function isDisplaySetReconstructable(instances) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (isMultiframe) {
|
if (isMultiframe) {
|
||||||
return processMultiframe(instances[0]);
|
return processMultiframe();
|
||||||
} else {
|
} else {
|
||||||
return processSingleframe(instances);
|
return processSingleframe(instances);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function processMultiframe(instance) {
|
/**
|
||||||
//TODO: deal with multriframe checks! return false for now as can't reconstruct.
|
* Process reconstructable multiframes checks
|
||||||
return { value: false };
|
* TODO: deal with multriframe checks! return false for now as can't reconstruct.
|
||||||
|
* *
|
||||||
|
* @returns {Object} value and reconstructionIssues.
|
||||||
|
*/
|
||||||
|
function processMultiframe() {
|
||||||
|
const reconstructionIssues = [ReconstructionIssues.MULTIFRAMES];
|
||||||
|
return { value: false, reconstructionIssues };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Process reconstructable single frame checks
|
||||||
|
*
|
||||||
|
* @param {Object[]} An array of `OHIFInstanceMetadata` objects.
|
||||||
|
*
|
||||||
|
* @returns {Object} value and reconstructionIssues.
|
||||||
|
*/
|
||||||
function processSingleframe(instances) {
|
function processSingleframe(instances) {
|
||||||
|
const n = instances.length;
|
||||||
const firstImage = instances[0].getData().metadata;
|
const firstImage = instances[0].getData().metadata;
|
||||||
const firstImageRows = firstImage.Rows;
|
const firstImageRows = firstImage.Rows;
|
||||||
const firstImageColumns = firstImage.Columns;
|
const firstImageColumns = firstImage.Columns;
|
||||||
const firstImageSamplesPerPixel = firstImage.SamplesPerPixel;
|
const firstImageSamplesPerPixel = firstImage.SamplesPerPixel;
|
||||||
const firstImageOrientationPatient = firstImage.ImageOrientationPatient;
|
const firstImageOrientationPatient = firstImage.ImageOrientationPatient;
|
||||||
const firstImagePositionPatient = firstImage.ImagePositionPatient;
|
|
||||||
|
|
||||||
|
const reconstructionIssues = [];
|
||||||
// Can't reconstruct if we:
|
// Can't reconstruct if we:
|
||||||
// -- Have a different dimensions within a displaySet.
|
// -- Have a different dimensions within a displaySet.
|
||||||
// -- Have a different number of components within a displaySet.
|
// -- Have a different number of components within a displaySet.
|
||||||
// -- Have different orientations within a displaySet.
|
// -- Have different orientations within a displaySet.
|
||||||
for (let i = 1; i < instances.length; i++) {
|
for (let ii = 1; ii < n; ++ii) {
|
||||||
const instance = instances[i].getData().metadata;
|
const instance = instances[ii].getData().metadata;
|
||||||
const {
|
const {
|
||||||
Rows,
|
Rows,
|
||||||
Columns,
|
Columns,
|
||||||
@@ -55,67 +74,146 @@ function processSingleframe(instances) {
|
|||||||
ImageOrientationPatient,
|
ImageOrientationPatient,
|
||||||
} = instance;
|
} = instance;
|
||||||
|
|
||||||
if (
|
if (Rows !== firstImageRows || Columns !== firstImageColumns) {
|
||||||
Rows !== firstImageRows ||
|
reconstructionIssues.push(ReconstructionIssues.VARYING_IMAGESDIMENSIONS);
|
||||||
Columns !== firstImageColumns ||
|
} else if (SamplesPerPixel !== firstImageSamplesPerPixel) {
|
||||||
SamplesPerPixel !== firstImageSamplesPerPixel ||
|
reconstructionIssues.push(ReconstructionIssues.VARYING_IMAGESCOMPONENTS);
|
||||||
!_isSameOrientation(ImageOrientationPatient, firstImageOrientationPatient)
|
} else if (!_isSameArray(ImageOrientationPatient, firstImageOrientationPatient)) {
|
||||||
) {
|
reconstructionIssues.push(ReconstructionIssues.VARYING_IMAGESORIENTATION);
|
||||||
return { value: false };
|
}
|
||||||
|
|
||||||
|
if (reconstructionIssues.length !== 0) {
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// check if dataset is 4D
|
||||||
|
if (_isDataset4D(instances)) {
|
||||||
|
reconstructionIssues.push(ReconstructionIssues.DATASET_4D);
|
||||||
|
}
|
||||||
|
|
||||||
|
return { value: reconstructionIssues.length === 0 ? true : false, reconstructionIssues };
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check is the spacing is uniform.
|
||||||
|
* The input metadata array has to be ordered by image position.
|
||||||
|
*
|
||||||
|
* @param {Object[]} An array of `OHIFInstanceMetadata` objects.
|
||||||
|
* @param {boolean} is the dataset 4D.
|
||||||
|
*
|
||||||
|
* @returns {Object} isUniform, reconstructionIssues and missingFrames
|
||||||
|
*/
|
||||||
|
function isSpacingUniform(instances, datasetIs4D) {
|
||||||
|
const n = instances.length;
|
||||||
|
const firstImage = instances[0].getData().metadata;
|
||||||
|
const firstImagePositionPatient = firstImage.ImagePositionPatient;
|
||||||
|
|
||||||
|
const reconstructionIssues = [];
|
||||||
let missingFrames = 0;
|
let missingFrames = 0;
|
||||||
|
|
||||||
// Check if frame spacing is approximately equal within a spacingTolerance.
|
// Check if frame spacing is approximately equal within a spacingTolerance.
|
||||||
// If spacing is on a uniform grid but we are missing frames,
|
// If spacing is on a uniform grid but we are missing frames,
|
||||||
// Allow reconstruction, but pass back the number of missing frames.
|
// Allow reconstruction, but pass back the number of missing frames.
|
||||||
if (instances.length > 2) {
|
if (n > 2) {
|
||||||
const lastIpp = instances[instances.length - 1].getData().metadata
|
const lastIpp = instances[n - 1].getData().metadata
|
||||||
.ImagePositionPatient;
|
.ImagePositionPatient;
|
||||||
|
|
||||||
// We can't reconstruct if we are missing ImagePositionPatient values
|
// We can't reconstruct if we are missing ImagePositionPatient values
|
||||||
if (!firstImagePositionPatient || !lastIpp) {
|
if (firstImagePositionPatient && lastIpp) {
|
||||||
return { value: false };
|
const averageSpacingBetweenFrames =
|
||||||
}
|
_getPerpendicularDistance(firstImagePositionPatient, lastIpp) /
|
||||||
|
(n - 1);
|
||||||
|
|
||||||
const averageSpacingBetweenFrames =
|
let previousImagePositionPatient = firstImagePositionPatient;
|
||||||
_getPerpendicularDistance(firstImagePositionPatient, lastIpp) /
|
|
||||||
(instances.length - 1);
|
|
||||||
|
|
||||||
let previousImagePositionPatient = firstImagePositionPatient;
|
for (let ii = 1; ii < n; ++ii) {
|
||||||
|
const instance = instances[ii].getData().metadata;
|
||||||
|
const { ImagePositionPatient } = instance;
|
||||||
|
|
||||||
for (let i = 1; i < instances.length; i++) {
|
const spacingBetweenFrames = _getPerpendicularDistance(
|
||||||
const instance = instances[i].getData().metadata;
|
ImagePositionPatient,
|
||||||
const { ImagePositionPatient } = instance;
|
previousImagePositionPatient
|
||||||
|
);
|
||||||
|
|
||||||
const spacingBetweenFrames = _getPerpendicularDistance(
|
if (datasetIs4D && spacingBetweenFrames < 1.e-3) {
|
||||||
ImagePositionPatient,
|
// the dataset is 4D, if the distance is zero, means that we are
|
||||||
previousImagePositionPatient
|
// checking the 4th dimension. Do not return, since we want still to
|
||||||
);
|
// check the 3rd dimension spacing.
|
||||||
const spacingIssue = _getSpacingIssue(
|
continue;
|
||||||
spacingBetweenFrames,
|
|
||||||
averageSpacingBetweenFrames
|
|
||||||
);
|
|
||||||
|
|
||||||
if (spacingIssue) {
|
|
||||||
const issue = spacingIssue.issue;
|
|
||||||
|
|
||||||
if (issue === reconstructionIssues.MISSING_FRAMES) {
|
|
||||||
missingFrames += spacingIssue.missingFrames;
|
|
||||||
} else if (issue === reconstructionIssues.IRREGULAR_SPACING) {
|
|
||||||
return { value: false };
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
previousImagePositionPatient = ImagePositionPatient;
|
const spacingIssue = _getSpacingIssue(
|
||||||
|
spacingBetweenFrames,
|
||||||
|
averageSpacingBetweenFrames
|
||||||
|
);
|
||||||
|
|
||||||
|
if (spacingIssue) {
|
||||||
|
const issue = spacingIssue.issue;
|
||||||
|
|
||||||
|
if (issue === ReconstructionIssues.MISSING_FRAMES) {
|
||||||
|
missingFrames += spacingIssue.missingFrames;
|
||||||
|
} else if (issue === ReconstructionIssues.IRREGULAR_SPACING) {
|
||||||
|
reconstructionIssues.push(issue);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
previousImagePositionPatient = ImagePositionPatient;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return { value: true, missingFrames };
|
return { isUniform: reconstructionIssues.length === 0 ? true : false, missingFrames, reconstructionIssues };
|
||||||
}
|
}
|
||||||
|
|
||||||
function _isSameOrientation(iop1, iop2) {
|
|
||||||
|
/**
|
||||||
|
* Check if 4D dataset.
|
||||||
|
*
|
||||||
|
* Assuming that slices at different time have the same position, here we just check if
|
||||||
|
* there are multiple slices for the same ImagePositionPatient and disable MPR.
|
||||||
|
*
|
||||||
|
* A better heuristic would be checking 4D tags, e.g. the presence of multiple TemporalPositionIdentifier values.
|
||||||
|
* However, some studies (e.g. https://github.com/OHIF/Viewers/issues/2113) do not have such tags.
|
||||||
|
*
|
||||||
|
* @param {Object[]} instances An array of `OHIFInstanceMetadata` objects.
|
||||||
|
*
|
||||||
|
* @returns {boolean} dataset4D value.
|
||||||
|
*/
|
||||||
|
function _isDataset4D(instances) {
|
||||||
|
const n = instances.length;
|
||||||
|
for (let ii = 0; ii < n; ++ii) {
|
||||||
|
const instanceMetadataControl = instances[ii].getData().metadata;
|
||||||
|
if (
|
||||||
|
!instanceMetadataControl ||
|
||||||
|
instanceMetadataControl === undefined ||
|
||||||
|
!instanceMetadataControl.ImagePositionPatient ||
|
||||||
|
instanceMetadataControl.ImagePositionPatient === undefined
|
||||||
|
) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
for (let jj = ii + 1; jj < n; ++jj) {
|
||||||
|
const instanceMetadata = instances[jj].getData().metadata;
|
||||||
|
if (
|
||||||
|
!instanceMetadata ||
|
||||||
|
instanceMetadata === undefined ||
|
||||||
|
!instanceMetadata.ImagePositionPatient ||
|
||||||
|
instanceMetadata.ImagePositionPatient === undefined
|
||||||
|
) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_isSameArray(instanceMetadataControl.ImagePositionPatient, instanceMetadata.ImagePositionPatient)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function _isSameArray(iop1, iop2) {
|
||||||
if (iop1 === undefined || !iop2 === undefined) {
|
if (iop1 === undefined || !iop2 === undefined) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -156,12 +254,12 @@ function _getSpacingIssue(spacing, averageSpacing) {
|
|||||||
|
|
||||||
if (errorForEachSpacing < spacingTolerance * averageSpacing) {
|
if (errorForEachSpacing < spacingTolerance * averageSpacing) {
|
||||||
return {
|
return {
|
||||||
issue: reconstructionIssues.MISSING_FRAMES,
|
issue: ReconstructionIssues.MISSING_FRAMES,
|
||||||
missingFrames: numberOfSpacings - 1,
|
missingFrames: numberOfSpacings - 1,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
return { issue: reconstructionIssues.IRREGULAR_SPACING };
|
return { issue: ReconstructionIssues.IRREGULAR_SPACING };
|
||||||
}
|
}
|
||||||
|
|
||||||
function _getPerpendicularDistance(a, b) {
|
function _getPerpendicularDistance(a, b) {
|
||||||
@@ -173,7 +271,5 @@ function _getPerpendicularDistance(a, b) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const constructableModalities = ['MR', 'CT', 'PT', 'NM'];
|
const constructableModalities = ['MR', 'CT', 'PT', 'NM'];
|
||||||
const reconstructionIssues = {
|
|
||||||
MISSING_FRAMES: 'missingframes',
|
export {isDisplaySetReconstructable, isSpacingUniform};
|
||||||
IRREGULAR_SPACING: 'irregularspacing',
|
|
||||||
};
|
|
||||||
@@ -45,13 +45,13 @@ import studyMetadataManager from './studyMetadataManager';
|
|||||||
* @param {string} referencedDisplaySet.studyInstanceUid
|
* @param {string} referencedDisplaySet.studyInstanceUid
|
||||||
* @param {Array} referencedDisplaySet.sopClassUids
|
* @param {Array} referencedDisplaySet.sopClassUids
|
||||||
* @param {Study[]} studies Collection of studies
|
* @param {Study[]} studies Collection of studies
|
||||||
|
* @param {object} logger
|
||||||
|
* @param {object} snackbar
|
||||||
* @returns void
|
* @returns void
|
||||||
*/
|
*/
|
||||||
const loadAndCacheDerivedDisplaySets = (referencedDisplaySet, studies) => {
|
async function loadAndCacheDerivedDisplaySets(referencedDisplaySet, studies, logger, snackbar) {
|
||||||
const { StudyInstanceUID, SeriesInstanceUID } = referencedDisplaySet;
|
const { StudyInstanceUID, SeriesInstanceUID } = referencedDisplaySet;
|
||||||
|
|
||||||
const promises = [];
|
const promises = [];
|
||||||
|
|
||||||
const studyMetadata = studyMetadataManager.get(StudyInstanceUID);
|
const studyMetadata = studyMetadataManager.get(StudyInstanceUID);
|
||||||
|
|
||||||
if (!studyMetadata) {
|
if (!studyMetadata) {
|
||||||
@@ -80,11 +80,10 @@ const loadAndCacheDerivedDisplaySets = (referencedDisplaySet, studies) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// For each type, see if any are loaded, if not load the most recent.
|
// For each type, see if any are loaded, if not load the most recent.
|
||||||
Object.keys(displaySetsPerModality).forEach(key => {
|
await Promise.all(Object.keys(displaySetsPerModality).map(async (key) => {
|
||||||
const displaySets = displaySetsPerModality[key];
|
const displaySets = displaySetsPerModality[key];
|
||||||
|
|
||||||
const isLoaded = displaySets.some(displaySet => displaySet.isLoaded);
|
const isLoaded = displaySets.some(displaySet => displaySet.isLoaded);
|
||||||
|
|
||||||
if (isLoaded) {
|
if (isLoaded) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -107,12 +106,34 @@ const loadAndCacheDerivedDisplaySets = (referencedDisplaySet, studies) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
recentDisplaySet.isLoading = true;
|
try {
|
||||||
|
if (recentDisplaySet.hasOwnProperty('getSourceDisplaySet') &&
|
||||||
|
typeof recentDisplaySet.getSourceDisplaySet === 'function') {
|
||||||
|
await recentDisplaySet.getSourceDisplaySet(studies);
|
||||||
|
} else {
|
||||||
|
await recentDisplaySet.load(referencedDisplaySet, studies);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
recentDisplaySet.isLoaded = false;
|
||||||
|
recentDisplaySet.loadError = true;
|
||||||
|
logger.error({ error, message: error.message });
|
||||||
|
snackbar.show({
|
||||||
|
title: 'Error loading derived display set:',
|
||||||
|
message: error.message,
|
||||||
|
type: 'error',
|
||||||
|
error,
|
||||||
|
autoClose: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
promises.push(recentDisplaySet.load(referencedDisplaySet, studies));
|
/*
|
||||||
});
|
* TODO: Improve the way we notify parts of the app
|
||||||
|
* that depends on derived display sets to be loaded.
|
||||||
return promises;
|
* (Implement pubsub for better tracking of derived display sets)
|
||||||
|
*/
|
||||||
|
const event = new CustomEvent('deriveddisplaysetsloadedandcached');
|
||||||
|
document.dispatchEvent(event);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default loadAndCacheDerivedDisplaySets;
|
export default loadAndCacheDerivedDisplaySets;
|
||||||
@@ -4,6 +4,7 @@ import str2ab from '../str2ab';
|
|||||||
import unpackOverlay from './unpackOverlay';
|
import unpackOverlay from './unpackOverlay';
|
||||||
|
|
||||||
import errorHandler from '../../errorHandler';
|
import errorHandler from '../../errorHandler';
|
||||||
|
import getXHRRetryRequestHook from '../xhrRetryRequestHook';
|
||||||
|
|
||||||
export default async function fetchOverlayData(instance, server) {
|
export default async function fetchOverlayData(instance, server) {
|
||||||
const OverlayDataPromises = [];
|
const OverlayDataPromises = [];
|
||||||
@@ -69,6 +70,7 @@ async function _getOverlayData(tag, server) {
|
|||||||
url: server.wadoRoot, //BulkDataURI is absolute, so this isn't used
|
url: server.wadoRoot, //BulkDataURI is absolute, so this isn't used
|
||||||
headers: DICOMWeb.getAuthorizationHeader(server),
|
headers: DICOMWeb.getAuthorizationHeader(server),
|
||||||
errorInterceptor: errorHandler.getHTTPErrorHandler(),
|
errorInterceptor: errorHandler.getHTTPErrorHandler(),
|
||||||
|
requestHooks: [getXHRRetryRequestHook()],
|
||||||
};
|
};
|
||||||
const dicomWeb = new api.DICOMwebClient(config);
|
const dicomWeb = new api.DICOMwebClient(config);
|
||||||
const options = {
|
const options = {
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import DICOMWeb from '../../DICOMWeb';
|
|||||||
import str2ab from '../str2ab';
|
import str2ab from '../str2ab';
|
||||||
|
|
||||||
import errorHandler from '../../errorHandler';
|
import errorHandler from '../../errorHandler';
|
||||||
|
import getXHRRetryRequestHook from '../xhrRetryRequestHook';
|
||||||
|
|
||||||
export default async function fetchPaletteColorLookupTableData(
|
export default async function fetchPaletteColorLookupTableData(
|
||||||
instance,
|
instance,
|
||||||
@@ -141,6 +142,7 @@ function _getPaletteColor(server, paletteColorLookupTableData, lutDescriptor) {
|
|||||||
url: server.wadoRoot, //BulkDataURI is absolute, so this isn't used
|
url: server.wadoRoot, //BulkDataURI is absolute, so this isn't used
|
||||||
headers: DICOMWeb.getAuthorizationHeader(server),
|
headers: DICOMWeb.getAuthorizationHeader(server),
|
||||||
errorInterceptor: errorHandler.getHTTPErrorHandler(),
|
errorInterceptor: errorHandler.getHTTPErrorHandler(),
|
||||||
|
requestHooks: [getXHRRetryRequestHook()],
|
||||||
};
|
};
|
||||||
const dicomWeb = new api.DICOMwebClient(config);
|
const dicomWeb = new api.DICOMwebClient(config);
|
||||||
const options = {
|
const options = {
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
const validNumber = val => {
|
||||||
|
if (Array.isArray(val)) {
|
||||||
|
return val.map(v => (v !== undefined ? Number(v) : v));
|
||||||
|
} else {
|
||||||
|
return val !== undefined ? Number(val) : val;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default validNumber;
|
||||||
@@ -0,0 +1,103 @@
|
|||||||
|
import retry from 'retry';
|
||||||
|
|
||||||
|
const defaultRetryOptions = {
|
||||||
|
retries: 5,
|
||||||
|
factor: 3,
|
||||||
|
minTimeout: 1 * 1000,
|
||||||
|
maxTimeout: 60 * 1000,
|
||||||
|
randomize: true,
|
||||||
|
retryableStatusCodes: [429, 500],
|
||||||
|
};
|
||||||
|
|
||||||
|
let retryOptions = { ...defaultRetryOptions };
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Request hook used to add retry functionality to XHR requests.
|
||||||
|
*
|
||||||
|
* @param {XMLHttpRequest} request XHR request instance
|
||||||
|
* @param {object} metadata Metadata about the request
|
||||||
|
* @param {object} metadata.url URL
|
||||||
|
* @param {object} metadata.method HTTP method
|
||||||
|
* @returns {XMLHttpRequest} request instance optionally modified
|
||||||
|
*/
|
||||||
|
const xhrRetryRequestHook = (request, metadata) => {
|
||||||
|
const { url, method } = metadata;
|
||||||
|
|
||||||
|
function faultTolerantRequestSend(...args) {
|
||||||
|
const operation = retry.operation(retryOptions);
|
||||||
|
|
||||||
|
operation.attempt(function operationAttempt(currentAttempt) {
|
||||||
|
const originalOnReadyStateChange = request.onreadystatechange;
|
||||||
|
|
||||||
|
/** Overriding/extending XHR function */
|
||||||
|
request.onreadystatechange = function onReadyStateChange(...args) {
|
||||||
|
originalOnReadyStateChange.apply(request, args);
|
||||||
|
|
||||||
|
if (retryOptions.retryableStatusCodes.includes(request.status)) {
|
||||||
|
const errorMessage = `Attempt to request ${url} failed.`;
|
||||||
|
const attemptFailedError = new Error(errorMessage);
|
||||||
|
operation.retry(attemptFailedError);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Call open only on retry (after headers and other things were set in the xhr instance) */
|
||||||
|
if (currentAttempt > 1) {
|
||||||
|
console.warn(`Requesting ${url}... (attempt: ${currentAttempt})`);
|
||||||
|
request.open(method, url, true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
originalRequestSend.apply(request, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Overriding/extending XHR function */
|
||||||
|
const originalRequestSend = request.send;
|
||||||
|
request.send = faultTolerantRequestSend;
|
||||||
|
|
||||||
|
return request;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a configured retry request hook function
|
||||||
|
* that can be used to add retry functionality to XHR request.
|
||||||
|
*
|
||||||
|
* Default options:
|
||||||
|
* retries: 5
|
||||||
|
* factor: 3
|
||||||
|
* minTimeout: 1 * 1000
|
||||||
|
* maxTimeout: 60 * 1000
|
||||||
|
* randomize: true
|
||||||
|
*
|
||||||
|
* @param {object} options
|
||||||
|
* @param {number} options.retires number of retries
|
||||||
|
* @param {number} options.factor factor
|
||||||
|
* @param {number} options.minTimeout the min timeout
|
||||||
|
* @param {number} options.maxTimeout the max timeout
|
||||||
|
* @param {boolean} options.randomize randomize
|
||||||
|
* @param {array} options.retryableStatusCodes status codes that can trigger retry
|
||||||
|
* @returns {function} the configured retry request function
|
||||||
|
*/
|
||||||
|
export const getXHRRetryRequestHook = (options = {}) => {
|
||||||
|
retryOptions = { ...defaultRetryOptions };
|
||||||
|
if ('retries' in options) {
|
||||||
|
retryOptions.retries = options.retries;
|
||||||
|
}
|
||||||
|
if ('factor' in options) {
|
||||||
|
retryOptions.factor = options.factor;
|
||||||
|
}
|
||||||
|
if ('minTimeout' in options) {
|
||||||
|
retryOptions.minTimeout = options.minTimeout;
|
||||||
|
}
|
||||||
|
if ('maxTimeout' in options) {
|
||||||
|
retryOptions.maxTimeout = options.maxTimeout;
|
||||||
|
}
|
||||||
|
if ('randomize' in options) {
|
||||||
|
retryOptions.randomize = options.randomize;
|
||||||
|
}
|
||||||
|
if ('retryableStatusCodes' in options) {
|
||||||
|
retryOptions.retryableStatusCodes = options.retryableStatusCodes;
|
||||||
|
}
|
||||||
|
return xhrRetryRequestHook;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default getXHRRetryRequestHook;
|
||||||
@@ -3,6 +3,49 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
## [1.9.3](https://github.com/OHIF/Viewers/compare/@ohif/ui@1.9.2...@ohif/ui@1.9.3) (2021-09-03)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/ui
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [1.9.2](https://github.com/OHIF/Viewers/compare/@ohif/ui@1.9.1...@ohif/ui@1.9.2) (2021-05-17)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/ui
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [1.9.1](https://github.com/OHIF/Viewers/compare/@ohif/ui@1.9.0...@ohif/ui@1.9.1) (2021-03-26)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/ui
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# [1.9.0](https://github.com/OHIF/Viewers/compare/@ohif/ui@1.8.2...@ohif/ui@1.9.0) (2021-03-04)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **log:** add new log service ([14d6454](https://github.com/OHIF/Viewers/commit/14d6454eafaa2ccb50e133c2945c9558052ea27e))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [1.8.2](https://github.com/OHIF/Viewers/compare/@ohif/ui@1.8.1...@ohif/ui@1.8.2) (2021-02-05)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/ui
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## [1.8.1](https://github.com/OHIF/Viewers/compare/@ohif/ui@1.8.0...@ohif/ui@1.8.1) (2021-01-21)
|
## [1.8.1](https://github.com/OHIF/Viewers/compare/@ohif/ui@1.8.0...@ohif/ui@1.8.1) (2021-01-21)
|
||||||
|
|
||||||
**Note:** Version bump only for package @ohif/ui
|
**Note:** Version bump only for package @ohif/ui
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@ohif/ui",
|
"name": "@ohif/ui",
|
||||||
"version": "1.8.1",
|
"version": "1.9.3",
|
||||||
"description": "A set of React components for Medical Imaging Viewers",
|
"description": "A set of React components for Medical Imaging Viewers",
|
||||||
"author": "OHIF Contributors",
|
"author": "OHIF Contributors",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
|||||||
@@ -32,6 +32,10 @@
|
|||||||
max-height: 90vh;
|
max-height: 90vh;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
|
|
||||||
|
&.noScroll &__content {
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
&:not(.visibleScrollbar) &__content
|
&:not(.visibleScrollbar) &__content
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
scrollbar-width: none;
|
scrollbar-width: none;
|
||||||
|
|||||||
@@ -4,6 +4,29 @@
|
|||||||
z-index: 0;
|
z-index: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.RoundedButtonGroup .badgeNumber-container {
|
||||||
|
background-color: var(--ui-border-color-dark);
|
||||||
|
width: 21px;
|
||||||
|
height: 21px;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
display: flex;
|
||||||
|
border-radius: 50%;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: bold;
|
||||||
|
bottom: 10px;
|
||||||
|
left: 5px;
|
||||||
|
color: white;
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
|
|
||||||
|
.RoundedButtonGroup .badgeNumber {
|
||||||
|
font-size: 10px;
|
||||||
|
font-weight: bold;
|
||||||
|
display: block;
|
||||||
|
width: 25px;
|
||||||
|
}
|
||||||
|
|
||||||
.roundedButtonWrapper {
|
.roundedButtonWrapper {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
@@ -36,7 +59,12 @@
|
|||||||
z-index: 1;
|
z-index: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.roundedButtonWrapper .roundedButton svg .roundedButtonWrapper .roundedButton span {
|
.roundedButtonWrapper
|
||||||
|
.roundedButton
|
||||||
|
svg
|
||||||
|
.roundedButtonWrapper
|
||||||
|
.roundedButton
|
||||||
|
span {
|
||||||
margin: 0 2px;
|
margin: 0 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ class RoundedButtonGroup extends Component {
|
|||||||
PropTypes.shape({
|
PropTypes.shape({
|
||||||
value: PropTypes.any,
|
value: PropTypes.any,
|
||||||
label: PropTypes.string,
|
label: PropTypes.string,
|
||||||
|
stateEvent: PropTypes.string,
|
||||||
icon: PropTypes.oneOfType([
|
icon: PropTypes.oneOfType([
|
||||||
PropTypes.string,
|
PropTypes.string,
|
||||||
PropTypes.shape({
|
PropTypes.shape({
|
||||||
@@ -30,6 +31,15 @@ class RoundedButtonGroup extends Component {
|
|||||||
value: null,
|
value: null,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
state = {
|
||||||
|
badgeNumbers: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
this.onStateEvent = this.onStateEvent.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
onClickOption = value => {
|
onClickOption = value => {
|
||||||
let newValue = value;
|
let newValue = value;
|
||||||
if (this.props.value === value) {
|
if (this.props.value === value) {
|
||||||
@@ -41,6 +51,48 @@ class RoundedButtonGroup extends Component {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
onStateEvent(event) {
|
||||||
|
const optionIndex = this.props.options.findIndex(
|
||||||
|
o => o.value === event.detail.target
|
||||||
|
);
|
||||||
|
if (optionIndex > -1) {
|
||||||
|
const badgeNumbers = this.state.badgeNumbers;
|
||||||
|
badgeNumbers[optionIndex] = event.detail.badgeNumber;
|
||||||
|
this.setState({ badgeNumbers });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
this.props.options.forEach(option => {
|
||||||
|
if (option.stateEvent) {
|
||||||
|
document.addEventListener(option.stateEvent, this.onStateEvent);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidUpdate(prevProps, prevState) {
|
||||||
|
this.props.options.forEach((option, index) => {
|
||||||
|
if (
|
||||||
|
option.stateEvent &&
|
||||||
|
option.stateEvent !==
|
||||||
|
(prevProps.options[index]
|
||||||
|
? prevProps.options[index].stateEvent
|
||||||
|
: null)
|
||||||
|
) {
|
||||||
|
document.removeEventListener(option.stateEvent, this.onStateEvent);
|
||||||
|
document.addEventListener(option.stateEvent, this.onStateEvent);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
this.props.options.forEach(option => {
|
||||||
|
if (option.stateEvent) {
|
||||||
|
document.removeEventListener(option.stateEvent, this.onStateEvent);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
let className = classnames(
|
let className = classnames(
|
||||||
RoundedButtonGroup.className,
|
RoundedButtonGroup.className,
|
||||||
@@ -62,6 +114,14 @@ class RoundedButtonGroup extends Component {
|
|||||||
<div className="bottomLabel">{option.bottomLabel}</div>
|
<div className="bottomLabel">{option.bottomLabel}</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let badgeNumber = this.state.badgeNumbers[index];
|
||||||
|
const badgeNumberOverflow = String(badgeNumber).length > 2;
|
||||||
|
badgeNumber = badgeNumber
|
||||||
|
? badgeNumberOverflow
|
||||||
|
? 99
|
||||||
|
: badgeNumber
|
||||||
|
: null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
key={index}
|
key={index}
|
||||||
@@ -70,6 +130,14 @@ class RoundedButtonGroup extends Component {
|
|||||||
>
|
>
|
||||||
<div className="roundedButton">
|
<div className="roundedButton">
|
||||||
{optionText}
|
{optionText}
|
||||||
|
{badgeNumber && (
|
||||||
|
<div className="badgeNumber-container">
|
||||||
|
<span className="badgeNumber">
|
||||||
|
{badgeNumber}
|
||||||
|
{badgeNumberOverflow && '+'}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
{iconProps && <Icon {...iconProps} />}
|
{iconProps && <Icon {...iconProps} />}
|
||||||
</div>
|
</div>
|
||||||
{bottomLabel}
|
{bottomLabel}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import './ImageThumbnail.styl';
|
|||||||
|
|
||||||
import { utils } from '@ohif/core';
|
import { utils } from '@ohif/core';
|
||||||
import React, { useState, useEffect, createRef } from 'react';
|
import React, { useState, useEffect, createRef } from 'react';
|
||||||
|
import classNames from 'classnames';
|
||||||
|
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import ViewportErrorIndicator from '../../viewer/ViewportErrorIndicator';
|
import ViewportErrorIndicator from '../../viewer/ViewportErrorIndicator';
|
||||||
@@ -15,6 +16,7 @@ import ViewportLoadingIndicator from '../../viewer/ViewportLoadingIndicator';
|
|||||||
//import cornerstone from 'cornerstone-core';
|
//import cornerstone from 'cornerstone-core';
|
||||||
function ImageThumbnail(props) {
|
function ImageThumbnail(props) {
|
||||||
const {
|
const {
|
||||||
|
active,
|
||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
imageSrc,
|
imageSrc,
|
||||||
@@ -97,7 +99,7 @@ function ImageThumbnail(props) {
|
|||||||
}, [fetchImagePromise, image.imageId, imageId, purgeCancelablePromise, setImagePromise]);
|
}, [fetchImagePromise, image.imageId, imageId, purgeCancelablePromise, setImagePromise]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="ImageThumbnail">
|
<div className={classNames('ImageThumbnail', { active: active })}>
|
||||||
<div className="image-thumbnail-canvas">
|
<div className="image-thumbnail-canvas">
|
||||||
{shouldRenderToCanvas() ? (
|
{shouldRenderToCanvas() ? (
|
||||||
<canvas ref={canvasRef} width={width} height={height} />
|
<canvas ref={canvasRef} width={width} height={height} />
|
||||||
@@ -126,6 +128,7 @@ function ImageThumbnail(props) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ImageThumbnail.propTypes = {
|
ImageThumbnail.propTypes = {
|
||||||
|
active: PropTypes.bool,
|
||||||
imageSrc: PropTypes.string,
|
imageSrc: PropTypes.string,
|
||||||
imageId: PropTypes.string,
|
imageId: PropTypes.string,
|
||||||
error: PropTypes.bool,
|
error: PropTypes.bool,
|
||||||
@@ -135,6 +138,7 @@ ImageThumbnail.propTypes = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
ImageThumbnail.defaultProps = {
|
ImageThumbnail.defaultProps = {
|
||||||
|
active: false,
|
||||||
error: false,
|
error: false,
|
||||||
stackPercentComplete: 0,
|
stackPercentComplete: 0,
|
||||||
width: 217,
|
width: 217,
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
--sidebar-transition: all 0.3s ease;
|
--sidebar-transition: all 0.3s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ThumbnailEntry.active .ImageThumbnail
|
.ImageThumbnail.active
|
||||||
border-color: var(--active-color);
|
border-color: var(--active-color);
|
||||||
box-shadow: none
|
box-shadow: none
|
||||||
transition: var(--sidebar-transition);
|
transition: var(--sidebar-transition);
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
.ImageThumbnail
|
.ImageThumbnail
|
||||||
background-color: var(--primary-background-color);
|
background-color: var(--primary-background-color);
|
||||||
box-shadow: inset 0 0 0 1px var(--ui-border-color-dark);
|
box-shadow: inset 0 0 0 1px var(--ui-border-color-dark);
|
||||||
border: 5px solid transparent
|
border: 2px solid transparent
|
||||||
border-radius: 12px
|
border-radius: 12px
|
||||||
height: 135px
|
height: 135px
|
||||||
margin: 0 auto
|
margin: 0 auto
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ function StudyBrowser(props) {
|
|||||||
return study.thumbnails.map((thumb, thumbIndex) => {
|
return study.thumbnails.map((thumb, thumbIndex) => {
|
||||||
// TODO: Thumb has more props than we care about?
|
// TODO: Thumb has more props than we care about?
|
||||||
const {
|
const {
|
||||||
|
active,
|
||||||
altImageText,
|
altImageText,
|
||||||
displaySetInstanceUID,
|
displaySetInstanceUID,
|
||||||
imageId,
|
imageId,
|
||||||
@@ -28,6 +29,7 @@ function StudyBrowser(props) {
|
|||||||
SeriesDescription,
|
SeriesDescription,
|
||||||
SeriesNumber,
|
SeriesNumber,
|
||||||
stackPercentComplete,
|
stackPercentComplete,
|
||||||
|
hasWarnings,
|
||||||
} = thumb;
|
} = thumb;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -37,6 +39,7 @@ function StudyBrowser(props) {
|
|||||||
data-cy="thumbnail-list"
|
data-cy="thumbnail-list"
|
||||||
>
|
>
|
||||||
<Thumbnail
|
<Thumbnail
|
||||||
|
active={active}
|
||||||
supportsDrag={supportsDrag}
|
supportsDrag={supportsDrag}
|
||||||
key={`${studyIndex}_${thumbIndex}`}
|
key={`${studyIndex}_${thumbIndex}`}
|
||||||
id={`${studyIndex}_${thumbIndex}`} // Unused?
|
id={`${studyIndex}_${thumbIndex}`} // Unused?
|
||||||
@@ -50,6 +53,7 @@ function StudyBrowser(props) {
|
|||||||
numImageFrames={numImageFrames}
|
numImageFrames={numImageFrames}
|
||||||
SeriesDescription={SeriesDescription}
|
SeriesDescription={SeriesDescription}
|
||||||
SeriesNumber={SeriesNumber}
|
SeriesNumber={SeriesNumber}
|
||||||
|
hasWarnings={hasWarnings}
|
||||||
stackPercentComplete={stackPercentComplete}
|
stackPercentComplete={stackPercentComplete}
|
||||||
// Events
|
// Events
|
||||||
onClick={onThumbnailClick.bind(
|
onClick={onThumbnailClick.bind(
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
import React from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { useDrag } from 'react-dnd';
|
import { useDrag } from 'react-dnd';
|
||||||
import ImageThumbnail from './ImageThumbnail';
|
import ImageThumbnail from './ImageThumbnail';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
|
import { Icon } from './../../elements/Icon';
|
||||||
|
import { Tooltip } from './../tooltip';
|
||||||
|
import { OverlayTrigger } from './../overlayTrigger';
|
||||||
|
|
||||||
import './Thumbnail.styl';
|
import './Thumbnail.styl';
|
||||||
|
|
||||||
@@ -11,7 +14,22 @@ function ThumbnailFooter({
|
|||||||
SeriesNumber,
|
SeriesNumber,
|
||||||
InstanceNumber,
|
InstanceNumber,
|
||||||
numImageFrames,
|
numImageFrames,
|
||||||
|
hasWarnings
|
||||||
}) {
|
}) {
|
||||||
|
const [inconsistencyWarnings, inconsistencyWarningsSet] = useState([]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
let unmounted = false
|
||||||
|
hasWarnings.then(response => {
|
||||||
|
if (!unmounted) {
|
||||||
|
inconsistencyWarningsSet(response)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return () => {
|
||||||
|
unmounted = true
|
||||||
|
}
|
||||||
|
}, [])
|
||||||
|
|
||||||
const infoOnly = !SeriesDescription;
|
const infoOnly = !SeriesDescription;
|
||||||
|
|
||||||
const getInfo = (value, icon, className = '') => {
|
const getInfo = (value, icon, className = '') => {
|
||||||
@@ -22,28 +40,73 @@ function ThumbnailFooter({
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const getWarningContent = (inconsistencyWarnings) => {
|
||||||
|
if (Array.isArray(inconsistencyWarnings)) {
|
||||||
|
const listedWarnings = inconsistencyWarnings.map((warn, index) => {
|
||||||
|
return <li key={index}>{warn}</li>;
|
||||||
|
});
|
||||||
|
|
||||||
|
return <ol>{listedWarnings}</ol>;
|
||||||
|
} else {
|
||||||
|
return <React.Fragment>{inconsistencyWarnings}</React.Fragment>;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const getWarningInfo = (SeriesNumber, inconsistencyWarnings) => {
|
||||||
|
return(
|
||||||
|
<React.Fragment>
|
||||||
|
{inconsistencyWarnings && inconsistencyWarnings.length != 0 ? (
|
||||||
|
<OverlayTrigger
|
||||||
|
key={SeriesNumber}
|
||||||
|
placement="left"
|
||||||
|
overlay={
|
||||||
|
<Tooltip
|
||||||
|
placement="left"
|
||||||
|
className="in tooltip-warning"
|
||||||
|
id="tooltip-left"
|
||||||
|
>
|
||||||
|
<div className="warningTitle">Series Inconsistencies</div>
|
||||||
|
<div className="warningContent">{getWarningContent(inconsistencyWarnings)}</div>
|
||||||
|
</Tooltip>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<div className={classNames('warning')}>
|
||||||
|
<span className="warning-icon">
|
||||||
|
<Icon name="exclamation-triangle" />
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</OverlayTrigger>
|
||||||
|
) : (
|
||||||
|
<React.Fragment></React.Fragment>
|
||||||
|
)}
|
||||||
|
</React.Fragment>
|
||||||
|
);
|
||||||
|
};
|
||||||
const getSeriesInformation = (
|
const getSeriesInformation = (
|
||||||
SeriesNumber,
|
SeriesNumber,
|
||||||
InstanceNumber,
|
InstanceNumber,
|
||||||
numImageFrames
|
numImageFrames,
|
||||||
|
inconsistencyWarnings
|
||||||
) => {
|
) => {
|
||||||
if (!SeriesNumber && !InstanceNumber && !numImageFrames) {
|
if (!SeriesNumber && !InstanceNumber && !numImageFrames) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
const seriesInformation =
|
||||||
return (
|
|
||||||
<div className="series-information">
|
<div className="series-information">
|
||||||
{getInfo(SeriesNumber, 'S:')}
|
{getInfo(SeriesNumber, 'S:')}
|
||||||
{getInfo(InstanceNumber, 'I:')}
|
{getInfo(InstanceNumber, 'I:')}
|
||||||
{getInfo(numImageFrames, '', 'image-frames')}
|
{getInfo(numImageFrames, '', 'image-frames')}
|
||||||
|
{getWarningInfo(SeriesNumber, inconsistencyWarnings)}
|
||||||
</div>
|
</div>
|
||||||
);
|
|
||||||
|
return (seriesInformation);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={classNames('series-details', { 'info-only': infoOnly })}>
|
<div className={classNames('series-details', { 'info-only': infoOnly })}>
|
||||||
<div className="series-description">{SeriesDescription}</div>
|
<div className="series-description">{SeriesDescription}</div>
|
||||||
{getSeriesInformation(SeriesNumber, InstanceNumber, numImageFrames)}
|
{getSeriesInformation(SeriesNumber, InstanceNumber, numImageFrames, inconsistencyWarnings)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -60,6 +123,7 @@ function Thumbnail(props) {
|
|||||||
numImageFrames,
|
numImageFrames,
|
||||||
SeriesDescription,
|
SeriesDescription,
|
||||||
SeriesNumber,
|
SeriesNumber,
|
||||||
|
hasWarnings,
|
||||||
stackPercentComplete,
|
stackPercentComplete,
|
||||||
StudyInstanceUID,
|
StudyInstanceUID,
|
||||||
onClick,
|
onClick,
|
||||||
@@ -95,6 +159,7 @@ function Thumbnail(props) {
|
|||||||
{/* SHOW IMAGE */}
|
{/* SHOW IMAGE */}
|
||||||
{hasImage && (
|
{hasImage && (
|
||||||
<ImageThumbnail
|
<ImageThumbnail
|
||||||
|
active={active}
|
||||||
imageSrc={imageSrc}
|
imageSrc={imageSrc}
|
||||||
imageId={imageId}
|
imageId={imageId}
|
||||||
error={error}
|
error={error}
|
||||||
@@ -126,13 +191,14 @@ Thumbnail.propTypes = {
|
|||||||
stackPercentComplete: PropTypes.number,
|
stackPercentComplete: PropTypes.number,
|
||||||
/**
|
/**
|
||||||
altImageText will be used when no imageId or imageSrc is provided.
|
altImageText will be used when no imageId or imageSrc is provided.
|
||||||
It will be displayed inside the <div>. This is useful when it is difficult
|
It will be displayed inside the <div>. This is useful when it is difficult
|
||||||
to make a preview for a type of DICOM series (e.g. DICOM-SR)
|
to make a preview for a type of DICOM series (e.g. DICOM-SR)
|
||||||
*/
|
*/
|
||||||
altImageText: PropTypes.string,
|
altImageText: PropTypes.string,
|
||||||
SeriesDescription: PropTypes.string,
|
SeriesDescription: PropTypes.string,
|
||||||
SeriesNumber: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
|
SeriesNumber: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
|
||||||
InstanceNumber: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
|
InstanceNumber: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
|
||||||
|
hasWarnings: PropTypes.instanceOf(Promise),
|
||||||
numImageFrames: PropTypes.number,
|
numImageFrames: PropTypes.number,
|
||||||
onDoubleClick: PropTypes.func,
|
onDoubleClick: PropTypes.func,
|
||||||
onClick: PropTypes.func,
|
onClick: PropTypes.func,
|
||||||
|
|||||||
@@ -2,8 +2,15 @@
|
|||||||
|
|
||||||
:root {
|
:root {
|
||||||
--series-count-background-color: #678696;
|
--series-count-background-color: #678696;
|
||||||
|
--active-color: #20A5D6;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.thumbnail.active
|
||||||
|
.alt-image-text
|
||||||
|
border-color: var(--active-color);
|
||||||
|
box-shadow: none
|
||||||
|
transition: var(--sidebar-transition);
|
||||||
|
|
||||||
.thumbnail
|
.thumbnail
|
||||||
cursor: pointer
|
cursor: pointer
|
||||||
display: table
|
display: table
|
||||||
@@ -16,7 +23,7 @@
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
background-color: var(--primary-background-color);
|
background-color: var(--primary-background-color);
|
||||||
box-shadow: inset 0 0 0 1px var(--ui-border-color-dark);
|
box-shadow: inset 0 0 0 1px var(--ui-border-color-dark);
|
||||||
border: 5px solid transparent;
|
border: 2px solid transparent;
|
||||||
border-radius: 12px;
|
border-radius: 12px;
|
||||||
height: 135px;
|
height: 135px;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
@@ -78,6 +85,17 @@
|
|||||||
height: 11px
|
height: 11px
|
||||||
width: 11px
|
width: 11px
|
||||||
|
|
||||||
|
.warning
|
||||||
|
margin: auto 0;
|
||||||
|
opacity: 1
|
||||||
|
color: #e29e4a;
|
||||||
|
|
||||||
|
svg
|
||||||
|
width: 16px;
|
||||||
|
height: 14px;
|
||||||
|
pointer-events: inherit;
|
||||||
|
|
||||||
|
|
||||||
.value
|
.value
|
||||||
color: var(--text-secondary-color);
|
color: var(--text-secondary-color);
|
||||||
display: inline-block
|
display: inline-block
|
||||||
|
|||||||
@@ -0,0 +1,104 @@
|
|||||||
|
import React, { useState, createContext, useContext, useEffect } from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
|
const LoggerContext = createContext(null);
|
||||||
|
const { Provider } = LoggerContext;
|
||||||
|
|
||||||
|
export const useLogger = () => useContext(LoggerContext);
|
||||||
|
|
||||||
|
const LoggerProvider = ({ children, service }) => {
|
||||||
|
const [state, setState] = useState({
|
||||||
|
errors: [],
|
||||||
|
infos: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const onErrorHandler = ({ error: errorObject, message }) => {
|
||||||
|
error({ error: errorObject, message });
|
||||||
|
};
|
||||||
|
window.addEventListener('error', onErrorHandler);
|
||||||
|
return () => {
|
||||||
|
window.removeEventListener('error', onErrorHandler);
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Logs an error
|
||||||
|
*
|
||||||
|
* @param {object} props { error, stack, message, displayOnConsole }
|
||||||
|
* @returns void
|
||||||
|
*/
|
||||||
|
const error = ({
|
||||||
|
error = {},
|
||||||
|
stack = '',
|
||||||
|
message = '',
|
||||||
|
displayOnConsole = true,
|
||||||
|
}) => {
|
||||||
|
const errorObject = { error, stack, message, displayOnConsole };
|
||||||
|
setState(state => ({ ...state, errors: [...state.errors, errorObject] }));
|
||||||
|
|
||||||
|
if (displayOnConsole) {
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Logs an info
|
||||||
|
*
|
||||||
|
* @param {object} props { message, displayOnConsole }
|
||||||
|
* @returns void
|
||||||
|
*/
|
||||||
|
const info = ({ message = '', displayOnConsole = true }) => {
|
||||||
|
setState(state => ({
|
||||||
|
...state,
|
||||||
|
infos: state.infos.push({ message, displayOnConsole }),
|
||||||
|
}));
|
||||||
|
|
||||||
|
if (displayOnConsole) {
|
||||||
|
console.info(message);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the implementation of a log service that can be used by extensions
|
||||||
|
*
|
||||||
|
* @returns void
|
||||||
|
*/
|
||||||
|
useEffect(() => {
|
||||||
|
if (service) {
|
||||||
|
service.setServiceImplementation({ error, info });
|
||||||
|
}
|
||||||
|
}, [error, service, info]);
|
||||||
|
|
||||||
|
return <Provider value={{ info, error, state }}>{children}</Provider>;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Higher Order Component to use the log methods through a Class Component
|
||||||
|
*
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export const withLogger = Component => {
|
||||||
|
return function WrappedComponent(props) {
|
||||||
|
const { error, info, state } = useLogger();
|
||||||
|
return <Component {...props} logger={{ error, info, state }} />;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
LoggerProvider.defaultProps = {
|
||||||
|
service: null,
|
||||||
|
};
|
||||||
|
|
||||||
|
LoggerProvider.propTypes = {
|
||||||
|
children: PropTypes.oneOfType([
|
||||||
|
PropTypes.arrayOf(PropTypes.node),
|
||||||
|
PropTypes.node,
|
||||||
|
]).isRequired,
|
||||||
|
service: PropTypes.shape({
|
||||||
|
setServiceImplementation: PropTypes.func,
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
||||||
|
export default LoggerProvider;
|
||||||
|
|
||||||
|
export const LogConsumer = LoggerContext.Consumer;
|
||||||
@@ -82,14 +82,20 @@ const ModalProvider = ({ children, modal: Modal, service }) => {
|
|||||||
shouldCloseOnEsc,
|
shouldCloseOnEsc,
|
||||||
fullscreen,
|
fullscreen,
|
||||||
closeButton,
|
closeButton,
|
||||||
showScrollbar
|
showScrollbar,
|
||||||
|
noScroll,
|
||||||
} = options;
|
} = options;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Provider value={{ show, hide }}>
|
<Provider value={{ show, hide }}>
|
||||||
{ModalContent && (
|
{ModalContent && (
|
||||||
<Modal
|
<Modal
|
||||||
className={classNames(customClassName, ModalContent.className, { 'visibleScrollbar': showScrollbar })}
|
className={classNames(
|
||||||
|
customClassName,
|
||||||
|
ModalContent.className,
|
||||||
|
{ visibleScrollbar: showScrollbar },
|
||||||
|
{ noScroll }
|
||||||
|
)}
|
||||||
shouldCloseOnEsc={shouldCloseOnEsc}
|
shouldCloseOnEsc={shouldCloseOnEsc}
|
||||||
isOpen={isOpen}
|
isOpen={isOpen}
|
||||||
title={title}
|
title={title}
|
||||||
|
|||||||
@@ -18,3 +18,8 @@ export {
|
|||||||
withDialog,
|
withDialog,
|
||||||
useDialog,
|
useDialog,
|
||||||
} from './DialogProvider.js';
|
} from './DialogProvider.js';
|
||||||
|
export {
|
||||||
|
default as LoggerProvider,
|
||||||
|
withLogger,
|
||||||
|
useLogger,
|
||||||
|
} from './LoggerProvider.js';
|
||||||
@@ -28,7 +28,13 @@ class Range extends Component {
|
|||||||
className="range"
|
className="range"
|
||||||
/>
|
/>
|
||||||
{this.props.showPercentage && <span>{`${this.state.value}%`}</span>}
|
{this.props.showPercentage && <span>{`${this.state.value}%`}</span>}
|
||||||
{this.props.showValue && <span>{this.state.value}</span>}
|
{this.props.showValue && (
|
||||||
|
<span>
|
||||||
|
{this.props.valueRenderer
|
||||||
|
? this.props.valueRenderer(this.state.value)
|
||||||
|
: this.state.value}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -40,6 +46,7 @@ Range.propTypes = {
|
|||||||
max: PropTypes.number.isRequired,
|
max: PropTypes.number.isRequired,
|
||||||
step: PropTypes.number,
|
step: PropTypes.number,
|
||||||
id: PropTypes.string,
|
id: PropTypes.string,
|
||||||
|
valueRenderer: PropTypes.func,
|
||||||
onChange: PropTypes.func,
|
onChange: PropTypes.func,
|
||||||
showPercentage: PropTypes.bool,
|
showPercentage: PropTypes.bool,
|
||||||
showValue: PropTypes.bool,
|
showValue: PropTypes.bool,
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ import {
|
|||||||
AboutContent,
|
AboutContent,
|
||||||
OHIFModal,
|
OHIFModal,
|
||||||
ErrorBoundary,
|
ErrorBoundary,
|
||||||
ErrorPage
|
ErrorPage,
|
||||||
} from './components';
|
} from './components';
|
||||||
import { useDebounce, useMedia } from './hooks';
|
import { useDebounce, useMedia } from './hooks';
|
||||||
|
|
||||||
@@ -66,6 +66,9 @@ import {
|
|||||||
ModalConsumer,
|
ModalConsumer,
|
||||||
useModal,
|
useModal,
|
||||||
withModal,
|
withModal,
|
||||||
|
LoggerProvider,
|
||||||
|
withLogger,
|
||||||
|
useLogger,
|
||||||
} from './contextProviders';
|
} from './contextProviders';
|
||||||
|
|
||||||
export {
|
export {
|
||||||
@@ -127,11 +130,14 @@ export {
|
|||||||
useDialog,
|
useDialog,
|
||||||
ErrorBoundary,
|
ErrorBoundary,
|
||||||
ErrorPage,
|
ErrorPage,
|
||||||
|
LoggerProvider,
|
||||||
|
withLogger,
|
||||||
|
useLogger,
|
||||||
// Hooks
|
// Hooks
|
||||||
useDebounce,
|
useDebounce,
|
||||||
useMedia,
|
useMedia,
|
||||||
// Utils
|
// Utils
|
||||||
ViewerbaseDragDropContext,
|
ViewerbaseDragDropContext,
|
||||||
asyncComponent,
|
asyncComponent,
|
||||||
retryImport
|
retryImport,
|
||||||
};
|
};
|
||||||
@@ -41,6 +41,13 @@ module.exports = (env, argv) => {
|
|||||||
path: DIST_DIR,
|
path: DIST_DIR,
|
||||||
filename: isProdBuild ? '[name].bundle.[chunkhash].js' : '[name].js',
|
filename: isProdBuild ? '[name].bundle.[chunkhash].js' : '[name].js',
|
||||||
publicPath: PUBLIC_URL, // Used by HtmlWebPackPlugin for asset prefix
|
publicPath: PUBLIC_URL, // Used by HtmlWebPackPlugin for asset prefix
|
||||||
|
devtoolModuleFilenameTemplate: function(info) {
|
||||||
|
if (isProdBuild) {
|
||||||
|
return `webpack:///${info.resourcePath}`;
|
||||||
|
} else {
|
||||||
|
return 'file:///' + encodeURI(info.absoluteResourcePath);
|
||||||
|
}
|
||||||
|
},
|
||||||
},
|
},
|
||||||
module: {
|
module: {
|
||||||
rules: [...extractStyleChunksRule(isProdBuild)],
|
rules: [...extractStyleChunksRule(isProdBuild)],
|
||||||
|
|||||||
@@ -3,6 +3,262 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
## [4.9.24](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.9.23...@ohif/viewer@4.9.24) (2021-09-03)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/viewer
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [4.9.23](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.9.22...@ohif/viewer@4.9.23) (2021-08-30)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* MPR metadata parsing for spacings ([#2517](https://github.com/OHIF/Viewers/issues/2517)) ([715dfc5](https://github.com/OHIF/Viewers/commit/715dfc556a3883de31d74012dcc99e431d91f57d))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [4.9.22](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.9.21...@ohif/viewer@4.9.22) (2021-08-19)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* Remove internet dependency for workbox (addressing [#2109](https://github.com/OHIF/Viewers/issues/2109)) ([#2151](https://github.com/OHIF/Viewers/issues/2151)) ([5f92f6e](https://github.com/OHIF/Viewers/commit/5f92f6ec7faa11d3fdc33ef8f22971a0bab87a36))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [4.9.21](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.9.20...@ohif/viewer@4.9.21) (2021-07-26)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/viewer
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [4.9.20](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.9.19...@ohif/viewer@4.9.20) (2021-06-03)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/viewer
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [4.9.19](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.9.18...@ohif/viewer@4.9.19) (2021-06-03)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/viewer
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [4.9.18](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.9.17...@ohif/viewer@4.9.18) (2021-06-02)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **dicom-html:** Add parsed dicom meta info section ([#2419](https://github.com/OHIF/Viewers/issues/2419)) ([403688b](https://github.com/OHIF/Viewers/commit/403688b18c52468fc1101166ec0c1734fb710039))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [4.9.17](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.9.16...@ohif/viewer@4.9.17) (2021-05-17)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/viewer
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [4.9.16](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.9.15...@ohif/viewer@4.9.16) (2021-05-14)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/viewer
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [4.9.15](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.9.14...@ohif/viewer@4.9.15) (2021-04-22)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/viewer
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [4.9.14](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.9.13...@ohif/viewer@4.9.14) (2021-04-22)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/viewer
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [4.9.13](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.9.12...@ohif/viewer@4.9.13) (2021-04-21)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/viewer
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [4.9.12](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.9.11...@ohif/viewer@4.9.12) (2021-04-21)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/viewer
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [4.9.11](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.9.10...@ohif/viewer@4.9.11) (2021-04-21)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/viewer
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [4.9.10](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.9.9...@ohif/viewer@4.9.10) (2021-04-16)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/viewer
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [4.9.9](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.9.8...@ohif/viewer@4.9.9) (2021-04-15)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/viewer
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [4.9.8](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.9.7...@ohif/viewer@4.9.8) (2021-03-31)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/viewer
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [4.9.7](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.9.6...@ohif/viewer@4.9.7) (2021-03-26)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/viewer
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [4.9.6](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.9.5...@ohif/viewer@4.9.6) (2021-03-26)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/viewer
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [4.9.5](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.9.4...@ohif/viewer@4.9.5) (2021-03-25)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* IDC-2288: Update toolbar tabs after derived displayset loaded ([#2307](https://github.com/OHIF/Viewers/issues/2307)) ([6e723eb](https://github.com/OHIF/Viewers/commit/6e723ebced58852007ca553792c740976cfc4201))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [4.9.4](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.9.3...@ohif/viewer@4.9.4) (2021-03-19)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/viewer
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [4.9.3](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.9.2...@ohif/viewer@4.9.3) (2021-03-15)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/viewer
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [4.9.2](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.9.1...@ohif/viewer@4.9.2) (2021-03-09)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/viewer
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [4.9.1](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.9.0...@ohif/viewer@4.9.1) (2021-03-09)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/viewer
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# [4.9.0](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.8.12...@ohif/viewer@4.9.0) (2021-03-04)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **log:** add new log service ([14d6454](https://github.com/OHIF/Viewers/commit/14d6454eafaa2ccb50e133c2945c9558052ea27e))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [4.8.12](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.8.11...@ohif/viewer@4.8.12) (2021-03-03)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/viewer
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [4.8.11](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.8.10...@ohif/viewer@4.8.11) (2021-02-25)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/viewer
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [4.8.10](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.8.9...@ohif/viewer@4.8.10) (2021-02-08)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/viewer
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [4.8.9](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.8.8...@ohif/viewer@4.8.9) (2021-02-05)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/viewer
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [4.8.8](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.8.7...@ohif/viewer@4.8.8) (2021-02-05)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/viewer
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## [4.8.7](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.8.6...@ohif/viewer@4.8.7) (2021-01-28)
|
## [4.8.7](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.8.6...@ohif/viewer@4.8.7) (2021-01-28)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -91,7 +91,7 @@ describe('OHIF Cornerstone Hotkeys', () => {
|
|||||||
':nth-child(2) > .viewport-wrapper > .viewport-element > .ViewportOrientationMarkers.noselect > .left-mid.orientation-marker'
|
':nth-child(2) > .viewport-wrapper > .viewport-element > .ViewportOrientationMarkers.noselect > .left-mid.orientation-marker'
|
||||||
).as('viewport2InfoMidLeft');
|
).as('viewport2InfoMidLeft');
|
||||||
cy.get(
|
cy.get(
|
||||||
':nth-child(2) > .viewport-wrapper > .viewport-element > .ViewportOverlay > div.bottom-right.overlay-element > div'
|
':nth-child(2) > .viewport-wrapper > .viewport-element > .OHIFCornerstoneViewportOverlay > div.bottom-right.overlay-element > div'
|
||||||
).as('viewport2InfoBottomRight');
|
).as('viewport2InfoBottomRight');
|
||||||
|
|
||||||
// Press multiples hotkeys on viewport #2
|
// Press multiples hotkeys on viewport #2
|
||||||
|
|||||||
@@ -741,7 +741,7 @@ describe('OHIF User Preferences', () => {
|
|||||||
|
|
||||||
// Overlay information from 2nd viewport
|
// Overlay information from 2nd viewport
|
||||||
let second_viewport_overlay =
|
let second_viewport_overlay =
|
||||||
'div:nth-child(2) > div > div.viewport-element > div.ViewportOverlay > div.bottom-right.overlay-element > div';
|
'div:nth-child(2) > div > div.viewport-element > div.OHIFCornerstoneViewportOverlay > div.bottom-right.overlay-element > div';
|
||||||
|
|
||||||
// Shift active viewport to Viewport #2
|
// Shift active viewport to Viewport #2
|
||||||
cy.get('body').type('{rightarrow}');
|
cy.get('body').type('{rightarrow}');
|
||||||
|
|||||||
Loaded 100 of 177 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user