Compare commits
233
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
35ed82e161 | ||
|
|
d0ccc22fd9 | ||
|
|
31841c495f | ||
|
|
65505d9bce | ||
|
|
460fdeb534 | ||
|
|
f4b7091ae4 | ||
|
|
1e72684455 | ||
|
|
5b0b6301b9 | ||
|
|
03a34a75a1 | ||
|
|
729272a985 | ||
|
|
9194ec2404 | ||
|
|
52c95a1209 | ||
|
|
8903adaea7 | ||
|
|
9481e2a99e | ||
|
|
f783fd7650 | ||
|
|
79afee7615 | ||
|
|
539141fe5a | ||
|
|
a046c6c901 | ||
|
|
073be065cf | ||
|
|
252536a1e6 | ||
|
|
53b1d71d56 | ||
|
|
702d99cb93 | ||
|
|
bb7b78bf33 | ||
|
|
b796fc9c62 | ||
|
|
66c587b139 | ||
|
|
f6e2e58aad | ||
|
|
489d4b0584 | ||
|
|
36019156da | ||
|
|
f2a371fc2d | ||
|
|
ad2f038817 | ||
|
|
8f2604509d | ||
|
|
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 | ||
|
|
d956963ef0 | ||
|
|
83b7d9e8b4 | ||
|
|
12e73228d1 | ||
|
|
4002401c66 | ||
|
|
18a8e059b6 | ||
|
|
f3582e514e | ||
|
|
c76bfd4a58 | ||
|
|
7f8cf5f5da | ||
|
|
5ef926477c | ||
|
|
87f5b97983 | ||
|
|
4c7b2b4ae0 | ||
|
|
d1e52dd6a6 | ||
|
|
cb147dfc69 | ||
|
|
a5fdeddfb4 | ||
|
|
e0e5f6000f | ||
|
|
cffb7d6f2f | ||
|
|
360616be13 | ||
|
|
d8fe8929b6 | ||
|
|
6b99a21227 | ||
|
|
16631e1fc4 | ||
|
|
587de94cea | ||
|
|
0ebe8a67d2 | ||
|
|
39a988dac7 | ||
|
|
f95928bcdd | ||
|
|
0f736bd294 | ||
|
|
6cbd9839c6 | ||
|
|
649bc5825a | ||
|
|
8038009afd | ||
|
|
f07f21117d | ||
|
|
77c7463c53 | ||
|
|
449e8f25f6 | ||
|
|
437c01511e | ||
|
|
00fddc1550 | ||
|
|
00b5cccae5 | ||
|
|
e545dd5b28 | ||
|
|
efde6ed20e | ||
|
|
32022f51f2 | ||
|
|
d282f72d75 | ||
|
|
29fceacee9 | ||
|
|
a7a5a359dc | ||
|
|
e362e895d4 | ||
|
|
45a07dfa19 | ||
|
|
7323e63608 | ||
|
|
0baf18dcb2 | ||
|
|
cca3d6bff2 | ||
|
|
f3cecf129b | ||
|
|
fe03fc6f55 | ||
|
|
b59187c74d | ||
|
|
5210c80369 | ||
|
|
f57725ac8b | ||
|
|
138e0fb3bf | ||
|
|
be494a7376 | ||
|
|
ab23feefbb | ||
|
|
4c079044f6 | ||
|
|
81fcfc7370 | ||
|
|
6ec6446787 | ||
|
|
4b5a43641a | ||
|
|
2fb0b01fe2 | ||
|
|
3632e5d495 | ||
|
|
dfe566e2aa | ||
|
|
6c5ad9e98d | ||
|
|
aeb7d5edcc | ||
|
|
d5b89748c9 | ||
|
|
b161016edb | ||
|
|
2fff2d511f | ||
|
|
cc92f91c1f | ||
|
|
d2e777bafc | ||
|
|
c0c3ab307d | ||
|
|
5121913ee6 | ||
|
|
6ef2b19925 | ||
|
|
f2450be255 | ||
|
|
04ca32a93e | ||
|
|
c92de6c647 | ||
|
|
5b302843d4 | ||
|
|
7eb8c4c641 | ||
|
|
619f361510 | ||
|
|
330eeaa216 | ||
|
|
d627dee3b2 | ||
|
|
d8714ea9c4 | ||
|
|
3c05920705 | ||
|
|
c6629bda75 | ||
|
|
588bcc9d17 | ||
|
|
c31f8f9d65 | ||
|
|
c0280e28ca |
No files matched your search
+76
-45
@@ -13,11 +13,23 @@ version: 2.1
|
|||||||
##
|
##
|
||||||
orbs:
|
orbs:
|
||||||
codecov: codecov/codecov@1.0.5
|
codecov: codecov/codecov@1.0.5
|
||||||
cypress: cypress-io/cypress@1.13.0
|
cypress: cypress-io/cypress@1.26.0
|
||||||
|
executors:
|
||||||
|
# Custom executor to override Cypress config
|
||||||
|
deploy-to-prod-executor:
|
||||||
|
docker:
|
||||||
|
- image: 'cypress/browsers:node14.15.0-chrome86-ff82'
|
||||||
|
environment:
|
||||||
|
CYPRESS_BASE_URL: https://ohif-staging.netlify.com/
|
||||||
|
chrome-and-pacs:
|
||||||
|
docker:
|
||||||
|
# Primary container image where all steps run.
|
||||||
|
- image: 'cypress/browsers:node14.15.0-chrome86-ff82'
|
||||||
|
- image: 'ohif/viewer-testdata:0.1-test'
|
||||||
|
|
||||||
defaults: &defaults
|
defaults: &defaults
|
||||||
docker:
|
docker:
|
||||||
- image: circleci/node:12.9.1
|
- image: circleci/node:14.15.0
|
||||||
environment:
|
environment:
|
||||||
TERM: xterm # Enable colors in term
|
TERM: xterm # Enable colors in term
|
||||||
QUICK_BUILD: true
|
QUICK_BUILD: true
|
||||||
@@ -36,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
|
||||||
@@ -95,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
|
||||||
@@ -131,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
|
||||||
@@ -170,7 +182,7 @@ jobs:
|
|||||||
|
|
||||||
DEPLOY_TO_DEV:
|
DEPLOY_TO_DEV:
|
||||||
docker:
|
docker:
|
||||||
- image: circleci/node:12.9.1
|
- image: circleci/node:14.15.0
|
||||||
environment:
|
environment:
|
||||||
TERM: xterm
|
TERM: xterm
|
||||||
NETLIFY_SITE_ID: 32708787-c9b0-4634-b50f-7ca41952da77
|
NETLIFY_SITE_ID: 32708787-c9b0-4634-b50f-7ca41952da77
|
||||||
@@ -185,7 +197,7 @@ jobs:
|
|||||||
|
|
||||||
DEPLOY_TO_STAGING:
|
DEPLOY_TO_STAGING:
|
||||||
docker:
|
docker:
|
||||||
- image: circleci/node:12.9.1
|
- image: circleci/node:14.15.0
|
||||||
environment:
|
environment:
|
||||||
TERM: xterm
|
TERM: xterm
|
||||||
NETLIFY_SITE_ID: c7502ae3-b150-493c-8422-05701e44a969
|
NETLIFY_SITE_ID: c7502ae3-b150-493c-8422-05701e44a969
|
||||||
@@ -200,7 +212,7 @@ jobs:
|
|||||||
|
|
||||||
DEPLOY_TO_PRODUCTION:
|
DEPLOY_TO_PRODUCTION:
|
||||||
docker:
|
docker:
|
||||||
- image: circleci/node:12.9.1
|
- image: circleci/node:14.15.0
|
||||||
environment:
|
environment:
|
||||||
TERM: xterm
|
TERM: xterm
|
||||||
NETLIFY_SITE_ID: 79c4a5da-5c95-4dc9-84f7-45fd9dfe21b0
|
NETLIFY_SITE_ID: 79c4a5da-5c95-4dc9-84f7-45fd9dfe21b0
|
||||||
@@ -225,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
|
||||||
@@ -257,7 +269,12 @@ jobs:
|
|||||||
paths: .
|
paths: .
|
||||||
|
|
||||||
DOCS_PUBLISH:
|
DOCS_PUBLISH:
|
||||||
<<: *defaults
|
docker:
|
||||||
|
# Gitbook fails due to graceful-fs updates above this node version :-(
|
||||||
|
- image: circleci/node:12.9.1
|
||||||
|
environment:
|
||||||
|
TERM: xterm # Enable colors in term
|
||||||
|
working_directory: ~/repo
|
||||||
steps:
|
steps:
|
||||||
- checkout
|
- checkout
|
||||||
- run:
|
- run:
|
||||||
@@ -317,16 +334,21 @@ workflows:
|
|||||||
# E2E: PWA
|
# E2E: PWA
|
||||||
- cypress/run:
|
- cypress/run:
|
||||||
name: 'E2E: PWA'
|
name: 'E2E: PWA'
|
||||||
executor: cypress/browsers-chrome76
|
executor: chrome-and-pacs
|
||||||
browser: chrome
|
browser: chrome
|
||||||
pre-steps:
|
pre-steps:
|
||||||
- run: 'rm -rf ~/.yarn && npm i -g yarn && yarn -v && yarn global
|
- run: |
|
||||||
add wait-on' # Use yarn latest
|
# Clear yarn cache; update to latest
|
||||||
|
rm -rf ~/.yarn
|
||||||
|
npm i -g yarn
|
||||||
|
yarn -v
|
||||||
yarn: true
|
yarn: true
|
||||||
record: false
|
record: true
|
||||||
store_artifacts: false
|
store_artifacts: true
|
||||||
working_directory: platform/viewer
|
working_directory: platform/viewer
|
||||||
build: npx cross-env QUICK_BUILD=true 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'
|
||||||
@@ -337,21 +359,24 @@ workflows:
|
|||||||
path: platform/viewer/cypress/screenshots
|
path: platform/viewer/cypress/screenshots
|
||||||
- store_artifacts:
|
- store_artifacts:
|
||||||
path: platform/viewer/cypress/videos
|
path: platform/viewer/cypress/videos
|
||||||
|
- store_test_results:
|
||||||
|
path: platform/viewer/cypress/results
|
||||||
requires:
|
requires:
|
||||||
- UNIT_TESTS
|
- UNIT_TESTS
|
||||||
# E2E: script-tag
|
# E2E: script-tag
|
||||||
- cypress/run:
|
- cypress/run:
|
||||||
name: 'E2E: Script Tag'
|
name: 'E2E: Script Tag'
|
||||||
executor: cypress/browsers-chrome76
|
executor: chrome-and-pacs
|
||||||
browser: chrome
|
browser: chrome
|
||||||
pre-steps:
|
pre-steps:
|
||||||
- run: 'rm -rf ~/.yarn && npm i -g yarn && yarn -v && yarn global
|
- run: 'rm -rf ~/.yarn && npm i -g yarn && yarn -v' # Use yarn latest
|
||||||
add wait-on' # Use yarn latest
|
|
||||||
yarn: true
|
yarn: true
|
||||||
record: false
|
record: true
|
||||||
store_artifacts: false
|
store_artifacts: true
|
||||||
working_directory: platform/viewer
|
working_directory: platform/viewer
|
||||||
build: npx cross-env QUICK_BUILD=true 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'
|
||||||
@@ -362,6 +387,8 @@ workflows:
|
|||||||
path: platform/viewer/cypress/screenshots
|
path: platform/viewer/cypress/screenshots
|
||||||
- store_artifacts:
|
- store_artifacts:
|
||||||
path: platform/viewer/cypress/videos
|
path: platform/viewer/cypress/videos
|
||||||
|
- store_test_results:
|
||||||
|
path: platform/viewer/cypress/results
|
||||||
requires:
|
requires:
|
||||||
- UNIT_TESTS
|
- UNIT_TESTS
|
||||||
|
|
||||||
@@ -372,7 +399,7 @@ workflows:
|
|||||||
# Update hub.docker.org
|
# Update hub.docker.org
|
||||||
- cypress/run:
|
- cypress/run:
|
||||||
name: 'Generate Percy Snapshots'
|
name: 'Generate Percy Snapshots'
|
||||||
executor: cypress/browsers-chrome76
|
executor: chrome-and-pacs
|
||||||
browser: chrome
|
browser: chrome
|
||||||
pre-steps:
|
pre-steps:
|
||||||
- run: 'rm -rf ~/.yarn && npm i -g yarn && yarn -v && yarn global
|
- run: 'rm -rf ~/.yarn && npm i -g yarn && yarn -v && yarn global
|
||||||
@@ -380,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 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" }}'
|
||||||
@@ -449,7 +478,7 @@ workflows:
|
|||||||
# and record a Cypress dashboard test run
|
# and record a Cypress dashboard test run
|
||||||
- cypress/run:
|
- cypress/run:
|
||||||
name: 'Generate Percy Snapshots'
|
name: 'Generate Percy Snapshots'
|
||||||
executor: cypress/browsers-chrome76
|
executor: chrome-and-pacs
|
||||||
browser: chrome
|
browser: chrome
|
||||||
pre-steps:
|
pre-steps:
|
||||||
- run: 'rm -rf ~/.yarn && npm i -g yarn && yarn -v && yarn global
|
- run: 'rm -rf ~/.yarn && npm i -g yarn && yarn -v && yarn global
|
||||||
@@ -457,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 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" }}'
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
custom: https://giving.massgeneral.org/ohif
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
---
|
|
||||||
name: "\U0001F917 Support Question"
|
|
||||||
about: "I have a question \U0001F4AC"
|
|
||||||
title: ''
|
|
||||||
labels: 'Community: Question :question:, Triage :white_flag:'
|
|
||||||
assignees: ''
|
|
||||||
---
|
|
||||||
|
|
||||||
> :hand: We are a small team with limited resources. Your question is much more
|
|
||||||
> likely to be answered if it is
|
|
||||||
> [a good question](https://stackoverflow.com/help/how-to-ask)
|
|
||||||
|
|
||||||
**Description**
|
|
||||||
|
|
||||||
Questions can often be answered by our documentation. Unable to find an answer
|
|
||||||
in our docs? We'll try to help. In the meantime, if you answer your own
|
|
||||||
question, please respond with the answer here so that others may benefit as
|
|
||||||
well. Better yet, open a PR to expand our docs ^\_^
|
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
blank_issues_enabled: false
|
||||||
|
contact_links:
|
||||||
|
- name: 🤗 Support Question
|
||||||
|
url: https://community.ohif.org/
|
||||||
|
about: Please use our forum if you have questions or need help.
|
||||||
+2
-2
@@ -2,9 +2,9 @@
|
|||||||
# https://github.com/apps/stale
|
# https://github.com/apps/stale
|
||||||
#
|
#
|
||||||
# Number of days of inactivity before an issue becomes stale
|
# Number of days of inactivity before an issue becomes stale
|
||||||
daysUntilStale: 21
|
daysUntilStale: 180
|
||||||
# Number of days of inactivity before a stale issue is closed
|
# Number of days of inactivity before a stale issue is closed
|
||||||
daysUntilClose: 7
|
daysUntilClose: 9000
|
||||||
# Issues with these labels will never be considered stale
|
# Issues with these labels will never be considered stale
|
||||||
exemptLabels:
|
exemptLabels:
|
||||||
- 'Story :raised_hands:'
|
- 'Story :raised_hands:'
|
||||||
|
|||||||
@@ -9,6 +9,10 @@ src/version.js
|
|||||||
junit.xml
|
junit.xml
|
||||||
coverage/
|
coverage/
|
||||||
.docz/
|
.docz/
|
||||||
|
*.js.map
|
||||||
|
.yarn
|
||||||
|
platform/docs/.docusaurus
|
||||||
|
platform/ui/storybook-static
|
||||||
|
|
||||||
# YALC (for Erik)
|
# YALC (for Erik)
|
||||||
.yalc
|
.yalc
|
||||||
@@ -34,3 +38,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}"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -3,7 +3,6 @@ const dotenv = require('dotenv');
|
|||||||
//
|
//
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
const webpack = require('webpack');
|
const webpack = require('webpack');
|
||||||
const PACKAGE = require('../platform/viewer/package.json');
|
|
||||||
// ~~ RULES
|
// ~~ RULES
|
||||||
const loadShadersRule = require('./rules/loadShaders.js');
|
const loadShadersRule = require('./rules/loadShaders.js');
|
||||||
const loadWebWorkersRule = require('./rules/loadWebWorkers.js');
|
const loadWebWorkersRule = require('./rules/loadWebWorkers.js');
|
||||||
@@ -77,7 +76,10 @@ module.exports = (env, argv, { SRC_DIR, DIST_DIR }) => {
|
|||||||
'process.env.DEBUG': JSON.stringify(process.env.DEBUG),
|
'process.env.DEBUG': JSON.stringify(process.env.DEBUG),
|
||||||
'process.env.APP_CONFIG': JSON.stringify(process.env.APP_CONFIG || ''),
|
'process.env.APP_CONFIG': JSON.stringify(process.env.APP_CONFIG || ''),
|
||||||
'process.env.PUBLIC_URL': JSON.stringify(process.env.PUBLIC_URL || '/'),
|
'process.env.PUBLIC_URL': JSON.stringify(process.env.PUBLIC_URL || '/'),
|
||||||
'process.env.VERSION_NUMBER': JSON.stringify(PACKAGE.version || ''),
|
'process.env.VERSION_NUMBER': webpack.DefinePlugin.runtimeValue(() => {
|
||||||
|
const package = require('../platform/viewer/package.json');
|
||||||
|
return JSON.stringify(package.version || '');
|
||||||
|
}, ['../platform/viewer/package.json']),
|
||||||
'process.env.BUILD_NUM': JSON.stringify(BUILD_NUM),
|
'process.env.BUILD_NUM': JSON.stringify(BUILD_NUM),
|
||||||
/* i18n */
|
/* i18n */
|
||||||
'process.env.USE_LOCIZE': JSON.stringify(process.env.USE_LOCIZE || ''),
|
'process.env.USE_LOCIZE': JSON.stringify(process.env.USE_LOCIZE || ''),
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -121,8 +121,7 @@ window.config = {
|
|||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
- Install the viewer:
|
- Install the viewer: `window.OHIFViewer.installViewer(window.config);`
|
||||||
`window.OHIFViewer.installViewer(window.config);`
|
|
||||||
|
|
||||||
This exact setup is demonstrated in this
|
This exact setup is demonstrated in this
|
||||||
[CodeSandbox](https://codesandbox.io/s/viewer-script-tag-tprch) and in our
|
[CodeSandbox](https://codesandbox.io/s/viewer-script-tag-tprch) and in our
|
||||||
@@ -259,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",
|
||||||
|
|||||||
@@ -67,6 +67,33 @@ window.config = {
|
|||||||
extensions: [],
|
extensions: [],
|
||||||
showStudyList: true,
|
showStudyList: true,
|
||||||
filterQueryParam: false,
|
filterQueryParam: false,
|
||||||
|
/**
|
||||||
|
* Disable caching of servers configuration.
|
||||||
|
*
|
||||||
|
* There will be no effect if you update the servers property of this config
|
||||||
|
* while your application is running because this property is cached in local storage.
|
||||||
|
*/
|
||||||
|
disableServersCache: false,
|
||||||
|
/**
|
||||||
|
* OHIF's study prefetcher configuration.
|
||||||
|
*
|
||||||
|
* @param {boolean} enabled Whether to enable/disable OHIF's study prefetcher
|
||||||
|
* @param {('all'|'closest'|'downward'|'upward'|'topdown')} order Fetching order: all display sets, the closest ones, downward or top down fashion based on the currently selected display set
|
||||||
|
* @param {number} displaySetCount How much display sets should be prefetched at once (note: this attribute is ignored if order was set to 'all')
|
||||||
|
* @param {boolean} preventCache Prevent images to be cached in Cornerstone Tools's request pool manager
|
||||||
|
* @param {number} prefetchDisplaySetsTimeout Prefetch timeout
|
||||||
|
* @param {boolean} displayProgress Whether to display or not the progress bar in the display set
|
||||||
|
* @param {boolean} includeActiveDisplaySet Include or not the active display set while prefetching
|
||||||
|
*/
|
||||||
|
studyPrefetcher: {
|
||||||
|
enabled: true,
|
||||||
|
order: 'all',
|
||||||
|
displaySetCount: 1,
|
||||||
|
preventCache: false,
|
||||||
|
prefetchDisplaySetsTimeout: 300,
|
||||||
|
displayProgress: false,
|
||||||
|
includeActiveDisplaySet: true,
|
||||||
|
},
|
||||||
servers: {
|
servers: {
|
||||||
dicomWeb: [
|
dicomWeb: [
|
||||||
{
|
{
|
||||||
@@ -81,7 +108,14 @@ window.config = {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
// Supported Keys: https://craig.is/killing/mice
|
/**
|
||||||
|
* Hotkey definitions.
|
||||||
|
* Supported Keys: https://craig.is/killing/mice
|
||||||
|
*
|
||||||
|
* @param {string} commandName
|
||||||
|
* @param {string} label
|
||||||
|
* @param {array} keys
|
||||||
|
*/
|
||||||
hotkeys: [
|
hotkeys: [
|
||||||
{ commandName: 'rotateViewportCW', label: 'Rotate Right', keys: ['r'] },
|
{ commandName: 'rotateViewportCW', label: 'Rotate Right', keys: ['r'] },
|
||||||
{ commandName: 'rotateViewportCCW', label: 'Rotate Left', keys: ['l'] },
|
{ commandName: 'rotateViewportCCW', label: 'Rotate Left', keys: ['l'] },
|
||||||
@@ -97,14 +131,16 @@ window.config = {
|
|||||||
keys: ['v'],
|
keys: ['v'],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
/* Configuration passed to the bundled cornerstone extension
|
/**
|
||||||
|
* Configuration passed to the bundled cornerstone extension
|
||||||
*
|
*
|
||||||
* The cornerstone extension is currently tightly coupled to the platform.
|
* The cornerstone extension is currently tightly coupled to the platform.
|
||||||
* Until we're able to decouple it, this key will serve as a workaround to
|
* Until we're able to decouple it, this key will serve as a workaround to
|
||||||
* pass it configuration.
|
* pass it configuration.
|
||||||
|
*
|
||||||
|
* @param {boolean} hideHandles Whether to show/hide annotation "handles"
|
||||||
*/
|
*/
|
||||||
cornerstoneExtensionConfig: {
|
cornerstoneExtensionConfig: {
|
||||||
/* Whether to show/hide annotation "handles" */
|
|
||||||
hideHandles: true,
|
hideHandles: true,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,37 @@
|
|||||||
|
# Viewer: Study Prefetcher & Stack Prefetch
|
||||||
|
|
||||||
|
Prefetching the stack of images of the active viewport is enabled by default for
|
||||||
|
cornerstone viewports via cornerstone tools's stack prefetch functionality.
|
||||||
|
|
||||||
|
In order to have a more customizable alternative for prefetching, OHIF provides
|
||||||
|
a built-in study prefetcher functionality which allows the user to set the order
|
||||||
|
in which the display sets are prefetched, the granularity, the timeout and
|
||||||
|
whether or not display a progress bar in the display set (close to the thumbnail
|
||||||
|
image) in the study browser.
|
||||||
|
|
||||||
|
You can customize these options via the viewer's configuration:
|
||||||
|
|
||||||
|
```js
|
||||||
|
window.config = {
|
||||||
|
/**
|
||||||
|
* OHIF's study prefetcher configuration.
|
||||||
|
*
|
||||||
|
* @param {boolean} enabled Whether to enable/disable OHIF's study prefetcher
|
||||||
|
* @param {('all'|'closest'|'downward'|'upward'|'topdown')} order Fetching order: all display sets, the closest ones, downward or top down fashion based on the currently selected display set
|
||||||
|
* @param {number} displaySetCount How much display sets should be prefetched at once (note: this attribute is ignored if order was set to 'all')
|
||||||
|
* @param {boolean} preventCache Prevent images to be cached in Cornerstone Tools's request pool manager
|
||||||
|
* @param {number} prefetchDisplaySetsTimeout Prefetch timeout
|
||||||
|
* @param {boolean} displayProgress Whether to display or not the progress bar in the display set
|
||||||
|
* @param {boolean} includeActiveDisplaySet Include or not the active display set while prefetching
|
||||||
|
*/
|
||||||
|
studyPrefetcher: {
|
||||||
|
enabled: true,
|
||||||
|
order: 'all',
|
||||||
|
displaySetCount: 1,
|
||||||
|
preventCache: false,
|
||||||
|
prefetchDisplaySetsTimeout: 300,
|
||||||
|
displayProgress: false,
|
||||||
|
includeActiveDisplaySet: true,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
```
|
||||||
+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,149 @@
|
|||||||
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.12.3](https://github.com/OHIF/Viewers/compare/@ohif/extension-cornerstone@2.12.2...@ohif/extension-cornerstone@2.12.3) (2021-12-02)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-cornerstone
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [2.12.2](https://github.com/OHIF/Viewers/compare/@ohif/extension-cornerstone@2.12.1...@ohif/extension-cornerstone@2.12.2) (2021-10-26)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-cornerstone
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [2.12.1](https://github.com/OHIF/Viewers/compare/@ohif/extension-cornerstone@2.12.0...@ohif/extension-cornerstone@2.12.1) (2021-09-28)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-cornerstone
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# [2.12.0](https://github.com/OHIF/Viewers/compare/@ohif/extension-cornerstone@2.11.0...@ohif/extension-cornerstone@2.12.0) (2021-09-23)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* Update to WADO Image Loader 4.0.3 to add dynamically imported WebAssembly codec support 🚀 ([#2537](https://github.com/OHIF/Viewers/issues/2537)) ([489d4b0](https://github.com/OHIF/Viewers/commit/489d4b0584f4657ceda1c5b9b68d5e9733827e47))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# [2.11.0](https://github.com/OHIF/Viewers/compare/@ohif/extension-cornerstone@2.10.10...@ohif/extension-cornerstone@2.11.0) (2021-09-09)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* OHIF-1000: Study prefetcher ([#2466](https://github.com/OHIF/Viewers/issues/2466)) ([8f26045](https://github.com/OHIF/Viewers/commit/8f2604509d24092b721f1333289d838820d68cac))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [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)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* visualize overlapping segments in cornerstone ([#2185](https://github.com/OHIF/Viewers/issues/2185)) ([29fceac](https://github.com/OHIF/Viewers/commit/29fceacee97d51f1952a0f6b574c66596d32c201))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## [2.9.6](https://github.com/OHIF/Viewers/compare/@ohif/extension-cornerstone@2.9.5...@ohif/extension-cornerstone@2.9.6) (2020-10-07)
|
## [2.9.6](https://github.com/OHIF/Viewers/compare/@ohif/extension-cornerstone@2.9.5...@ohif/extension-cornerstone@2.9.6) (2020-10-07)
|
||||||
|
|
||||||
**Note:** Version bump only for package @ohif/extension-cornerstone
|
**Note:** Version bump only for package @ohif/extension-cornerstone
|
||||||
|
|||||||
@@ -85,12 +85,32 @@ Tools can be configured through extension configuration using the tools key:
|
|||||||
...
|
...
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Stack prefetch configuration
|
||||||
|
|
||||||
|
Stack prefetch can be configured through extension configuration using the
|
||||||
|
stackPrefetch key:
|
||||||
|
|
||||||
|
```js
|
||||||
|
...
|
||||||
|
cornerstoneExtensionConfig: {
|
||||||
|
stackPrefetch: {
|
||||||
|
enabled: true,
|
||||||
|
maxImagesToPrefetch: Infinity,
|
||||||
|
preserveExistingPool: false,
|
||||||
|
maxSimultaneousRequests: 20,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
## Annotate Tools Configuration
|
## Annotate Tools Configuration
|
||||||
|
|
||||||
*We currently support one property for annotation tools.*
|
_We currently support one property for annotation tools._
|
||||||
|
|
||||||
### Hide handles
|
### Hide handles
|
||||||
This extension configuration allows you to toggle on/off handle rendering for all annotate tools:
|
|
||||||
|
This extension configuration allows you to toggle on/off handle rendering for
|
||||||
|
all annotate tools:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
...
|
...
|
||||||
@@ -119,3 +139,4 @@ This extension configuration allows you to toggle on/off handle rendering for al
|
|||||||
[cornerstone-tools]: https://github.com/cornerstonejs/cornerstoneTools
|
[cornerstone-tools]: https://github.com/cornerstonejs/cornerstoneTools
|
||||||
[cornerstone]: https://github.com/cornerstonejs/cornerstone
|
[cornerstone]: https://github.com/cornerstonejs/cornerstone
|
||||||
<!-- prettier-ignore-end -->
|
<!-- prettier-ignore-end -->
|
||||||
|
```
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@ohif/extension-cornerstone",
|
"name": "@ohif/extension-cornerstone",
|
||||||
"version": "2.9.6",
|
"version": "2.12.3",
|
||||||
"description": "OHIF extension for Cornerstone",
|
"description": "OHIF extension for Cornerstone",
|
||||||
"author": "OHIF",
|
"author": "OHIF",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
@@ -32,12 +32,12 @@
|
|||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@ohif/core": "^0.50.0",
|
"@ohif/core": "^0.50.0",
|
||||||
"@ohif/ui": "^0.50.0",
|
"@ohif/ui": "^0.50.0",
|
||||||
"cornerstone-core": "^2.2.8",
|
"cornerstone-core": "^2.6.1",
|
||||||
"cornerstone-math": "^0.1.8",
|
"cornerstone-math": "^0.1.9",
|
||||||
"cornerstone-tools": "^4.20.1",
|
"cornerstone-tools": "^6.0.6",
|
||||||
"cornerstone-wado-image-loader": "^3.1.0",
|
"cornerstone-wado-image-loader": "^4.1.0",
|
||||||
"dcmjs": "0.16.5",
|
"dcmjs": "0.19.1",
|
||||||
"dicom-parser": "^1.8.3",
|
"dicom-parser": "^1.8.11",
|
||||||
"hammerjs": "^2.0.8",
|
"hammerjs": "^2.0.8",
|
||||||
"prop-types": "^15.6.2",
|
"prop-types": "^15.6.2",
|
||||||
"react": "^16.8.6",
|
"react": "^16.8.6",
|
||||||
@@ -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.1.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -53,7 +53,11 @@ const mapStateToProps = (state, ownProps) => {
|
|||||||
// Currently justing using escape hatch + commands
|
// Currently justing using escape hatch + commands
|
||||||
// activeTool: activeButton && activeButton.command,
|
// activeTool: activeButton && activeButton.command,
|
||||||
...dataFromStore,
|
...dataFromStore,
|
||||||
isStackPrefetchEnabled: isActive,
|
isStackPrefetchEnabled: ownProps.hasOwnProperty('isStackPrefetchEnabled')
|
||||||
|
? ownProps.isStackPrefetchEnabled
|
||||||
|
: ownProps.stackPrefetch
|
||||||
|
? ownProps.stackPrefetch.enabled
|
||||||
|
: isActive,
|
||||||
isPlaying,
|
isPlaying,
|
||||||
frameRate,
|
frameRate,
|
||||||
//stack: viewportSpecificData.stack,
|
//stack: viewportSpecificData.stack,
|
||||||
|
|||||||
@@ -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';
|
||||||
@@ -14,6 +15,7 @@ class OHIFCornerstoneViewport extends Component {
|
|||||||
|
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
customProps: {},
|
customProps: {},
|
||||||
|
isStackPrefetchEnabled: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
@@ -22,6 +24,8 @@ class OHIFCornerstoneViewport extends Component {
|
|||||||
viewportIndex: PropTypes.number,
|
viewportIndex: PropTypes.number,
|
||||||
children: PropTypes.node,
|
children: PropTypes.node,
|
||||||
customProps: PropTypes.object,
|
customProps: PropTypes.object,
|
||||||
|
stackPrefetch: PropTypes.object,
|
||||||
|
isStackPrefetchEnabled: PropTypes.bool,
|
||||||
};
|
};
|
||||||
|
|
||||||
static id = 'OHIFCornerstoneViewport';
|
static id = 'OHIFCornerstoneViewport';
|
||||||
@@ -179,7 +183,7 @@ class OHIFCornerstoneViewport 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
|
||||||
) {
|
) {
|
||||||
@@ -194,6 +198,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 +224,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,18 +234,29 @@ 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}
|
||||||
|
stackPrefetch={this.props.stackPrefetch}
|
||||||
|
isStackPrefetchEnabled={this.props.isStackPrefetchEnabled}
|
||||||
// ~~ Connected (From REDUX)
|
// ~~ Connected (From REDUX)
|
||||||
// frameRate={frameRate}
|
// frameRate={frameRate}
|
||||||
// isPlaying={false}
|
// isPlaying={false}
|
||||||
// isStackPrefetchEnabled={true}
|
|
||||||
// onElementEnabled={() => {}}
|
// onElementEnabled={() => {}}
|
||||||
// setViewportActive{() => {}}
|
// setViewportActive{() => {}}
|
||||||
{...this.props.customProps}
|
{...this.props.customProps}
|
||||||
|
|||||||
@@ -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;
|
||||||
@@ -36,13 +36,29 @@ export default {
|
|||||||
preRegistration({ servicesManager, configuration = {} }) {
|
preRegistration({ servicesManager, configuration = {} }) {
|
||||||
init({ servicesManager, configuration });
|
init({ servicesManager, configuration });
|
||||||
},
|
},
|
||||||
getViewportModule({ commandsManager }) {
|
getViewportModule({ commandsManager, appConfig }) {
|
||||||
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);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const { studyPrefetcher } = appConfig;
|
||||||
|
const isStackPrefetchEnabled =
|
||||||
|
studyPrefetcher && !studyPrefetcher.enabled;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<OHIFCornerstoneViewport {...props} onNewImage={onNewImageHandler} />
|
<OHIFCornerstoneViewport
|
||||||
|
{...props}
|
||||||
|
onNewImage={onNewImageHandler}
|
||||||
|
isStackPrefetchEnabled={isStackPrefetchEnabled}
|
||||||
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -37,7 +37,14 @@ export default function init({ servicesManager, configuration }) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const { csToolsConfig } = configuration;
|
const {
|
||||||
|
csToolsConfig,
|
||||||
|
stackPrefetch = {
|
||||||
|
maxImagesToPrefetch: Infinity,
|
||||||
|
preserveExistingPool: false,
|
||||||
|
maxSimultaneousRequests: 20,
|
||||||
|
},
|
||||||
|
} = configuration;
|
||||||
const metadataProvider = OHIF.cornerstone.metadataProvider;
|
const metadataProvider = OHIF.cornerstone.metadataProvider;
|
||||||
|
|
||||||
cornerstone.metaData.addProvider(
|
cornerstone.metaData.addProvider(
|
||||||
@@ -52,7 +59,7 @@ export default function init({ servicesManager, configuration }) {
|
|||||||
autoResizeViewports: false,
|
autoResizeViewports: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
initCornerstoneTools(defaultCsToolsConfig);
|
initCornerstoneTools({ ...defaultCsToolsConfig, ...stackPrefetch });
|
||||||
|
|
||||||
const toolsGroupedByType = {
|
const toolsGroupedByType = {
|
||||||
touch: [csTools.PanMultiTouchTool, csTools.ZoomTouchPinchTool],
|
touch: [csTools.PanMultiTouchTool, csTools.ZoomTouchPinchTool],
|
||||||
|
|||||||
@@ -37,8 +37,8 @@ export default function(configuration = {}) {
|
|||||||
|
|
||||||
// Configure stack prefetch
|
// Configure stack prefetch
|
||||||
cornerstoneTools.stackPrefetch.setConfiguration({
|
cornerstoneTools.stackPrefetch.setConfiguration({
|
||||||
maxImagesToPrefetch: Infinity,
|
maxImagesToPrefetch: configuration.maxImagesToPrefetch,
|
||||||
preserveExistingPool: false,
|
preserveExistingPool: configuration.preserveExistingPool,
|
||||||
maxSimultaneousRequests: 20,
|
maxSimultaneousRequests: configuration.maxSimultaneousRequests,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -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,52 @@
|
|||||||
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.3.1](https://github.com/OHIF/Viewers/compare/@ohif/extension-debugging@0.3.0...@ohif/extension-debugging@0.3.1) (2021-12-02)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-debugging
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# [0.3.0](https://github.com/OHIF/Viewers/compare/@ohif/extension-debugging@0.2.2...@ohif/extension-debugging@0.3.0) (2021-09-23)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* Update to WADO Image Loader 4.0.3 to add dynamically imported WebAssembly codec support 🚀 ([#2537](https://github.com/OHIF/Viewers/issues/2537)) ([489d4b0](https://github.com/OHIF/Viewers/commit/489d4b0584f4657ceda1c5b9b68d5e9733827e47))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [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.3.1",
|
||||||
"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.11"
|
||||||
"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.3",
|
||||||
"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,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.
|
||||||
|
|
||||||
|
## [1.3.14](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-html@1.3.13...@ohif/extension-dicom-html@1.3.14) (2021-12-02)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-dicom-html
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [1.3.13](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-html@1.3.12...@ohif/extension-dicom-html@1.3.13) (2021-10-26)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-dicom-html
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [1.3.12](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-html@1.3.11...@ohif/extension-dicom-html@1.3.12) (2021-10-25)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-dicom-html
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [1.3.11](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-html@1.3.10...@ohif/extension-dicom-html@1.3.11) (2021-09-28)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-dicom-html
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [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)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* visualize overlapping segments in cornerstone ([#2185](https://github.com/OHIF/Viewers/issues/2185)) ([29fceac](https://github.com/OHIF/Viewers/commit/29fceacee97d51f1952a0f6b574c66596d32c201))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## [1.2.9](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-html@1.2.8...@ohif/extension-dicom-html@1.2.9) (2020-10-07)
|
## [1.2.9](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-html@1.2.8...@ohif/extension-dicom-html@1.2.9) (2020-10-07)
|
||||||
|
|
||||||
**Note:** Version bump only for package @ohif/extension-dicom-html
|
**Note:** Version bump only for package @ohif/extension-dicom-html
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@ohif/extension-dicom-html",
|
"name": "@ohif/extension-dicom-html",
|
||||||
"version": "1.2.9",
|
"version": "1.3.14",
|
||||||
"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.16.5",
|
"dcmjs": "0.19.1",
|
||||||
"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"
|
||||||
|
|||||||
@@ -160,7 +160,7 @@ const getContentSequence = (data, level = 1) => {
|
|||||||
|
|
||||||
const root = [];
|
const root = [];
|
||||||
if (header) {
|
if (header) {
|
||||||
const HeaderDynamicLevel = `h${level}`;
|
const HeaderDynamicLevel = `h${Math.min(level, 6)}`;
|
||||||
|
|
||||||
root.push(<HeaderDynamicLevel key={header}>{header}</HeaderDynamicLevel>);
|
root.push(<HeaderDynamicLevel key={header}>{header}</HeaderDynamicLevel>);
|
||||||
}
|
}
|
||||||
@@ -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 key={root.length} />);
|
||||||
|
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);
|
||||||
@@ -207,7 +217,7 @@ class DicomHtmlViewport extends Component {
|
|||||||
};
|
};
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
const dataSet = this.setContentFromByteArray(this.props.byteArray);
|
this.setContentFromByteArray(this.props.byteArray);
|
||||||
}
|
}
|
||||||
|
|
||||||
setContentFromByteArray(byteArray) {
|
setContentFromByteArray(byteArray) {
|
||||||
@@ -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,25 @@
|
|||||||
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)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-dicom-microscopy
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## [0.51.3](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-microscopy@0.51.2...@ohif/extension-dicom-microscopy@0.51.3) (2020-09-10)
|
## [0.51.3](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-microscopy@0.51.2...@ohif/extension-dicom-microscopy@0.51.3) (2020-09-10)
|
||||||
|
|
||||||
**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.3",
|
"version": "0.52.0",
|
||||||
"description": "OHIF extension for Dicom Microscopy",
|
"description": "OHIF extension for Dicom Microscopy",
|
||||||
"author": "OHIF",
|
"author": "OHIF",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
|||||||
@@ -62,11 +62,30 @@ class DicomMicroscopyViewport extends Component {
|
|||||||
);
|
);
|
||||||
const microscopyViewer = api.VLWholeSlideMicroscopyImageViewer;
|
const microscopyViewer = api.VLWholeSlideMicroscopyImageViewer;
|
||||||
|
|
||||||
this.viewer = new microscopyViewer({
|
try {
|
||||||
client: dicomWebClient,
|
this.viewer = new microscopyViewer({
|
||||||
metadata,
|
client: dicomWebClient,
|
||||||
retrieveRendered: false,
|
metadata,
|
||||||
});
|
retrieveRendered: false,
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error('[Microscopy Viewer] Failed to load:', error);
|
||||||
|
const {
|
||||||
|
UINotificationService,
|
||||||
|
LoggerService,
|
||||||
|
} = this.props.servicesManager.services;
|
||||||
|
if (UINotificationService) {
|
||||||
|
const message =
|
||||||
|
'Failed to load viewport. Please check that you have hardware acceleration enabled.';
|
||||||
|
LoggerService.error({ error, message });
|
||||||
|
UINotificationService.show({
|
||||||
|
autoClose: false,
|
||||||
|
title: 'Microscopy Viewport',
|
||||||
|
message,
|
||||||
|
type: 'error',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
this.viewer.render({ container });
|
this.viewer.render({ container });
|
||||||
});
|
});
|
||||||
@@ -92,8 +111,8 @@ class DicomMicroscopyViewport extends Component {
|
|||||||
{this.state.error ? (
|
{this.state.error ? (
|
||||||
<h2>{JSON.stringify(this.state.error)}</h2>
|
<h2>{JSON.stringify(this.state.error)}</h2>
|
||||||
) : (
|
) : (
|
||||||
<div style={style} ref={this.container} />
|
<div style={style} ref={this.container} />
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,14 +6,6 @@ const Component = React.lazy(() => {
|
|||||||
return import('./DicomMicroscopyViewport');
|
return import('./DicomMicroscopyViewport');
|
||||||
});
|
});
|
||||||
|
|
||||||
const DicomMicroscopyViewport = props => {
|
|
||||||
return (
|
|
||||||
<React.Suspense fallback={<div>Loading...</div>}>
|
|
||||||
<Component {...props} />
|
|
||||||
</React.Suspense>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
/**
|
/**
|
||||||
* Only required property. Should be a unique value across all extensions.
|
* Only required property. Should be a unique value across all extensions.
|
||||||
@@ -21,8 +13,14 @@ export default {
|
|||||||
id: 'microscopy',
|
id: 'microscopy',
|
||||||
version,
|
version,
|
||||||
|
|
||||||
getViewportModule() {
|
getViewportModule({ servicesManager }) {
|
||||||
return DicomMicroscopyViewport;
|
return props => {
|
||||||
|
return (
|
||||||
|
<React.Suspense fallback={<div>Loading...</div>}>
|
||||||
|
<Component {...props} servicesManager={servicesManager} />
|
||||||
|
</React.Suspense>
|
||||||
|
);
|
||||||
|
};
|
||||||
},
|
},
|
||||||
getSopClassHandlerModule() {
|
getSopClassHandlerModule() {
|
||||||
return DicomMicroscopySopClassHandler;
|
return DicomMicroscopySopClassHandler;
|
||||||
|
|||||||
@@ -3,6 +3,25 @@
|
|||||||
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.1.1](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-pdf@1.1.0...@ohif/extension-dicom-pdf@1.1.1) (2021-12-02)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-dicom-pdf
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# [1.1.0](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-pdf@1.0.6...@ohif/extension-dicom-pdf@1.1.0) (2021-09-23)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* Update to WADO Image Loader 4.0.3 to add dynamically imported WebAssembly codec support 🚀 ([#2537](https://github.com/OHIF/Viewers/issues/2537)) ([489d4b0](https://github.com/OHIF/Viewers/commit/489d4b0584f4657ceda1c5b9b68d5e9733827e47))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## [1.0.6](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-pdf@1.0.5...@ohif/extension-dicom-pdf@1.0.6) (2020-09-10)
|
## [1.0.6](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-pdf@1.0.5...@ohif/extension-dicom-pdf@1.0.6) (2020-09-10)
|
||||||
|
|
||||||
**Note:** Version bump only for package @ohif/extension-dicom-pdf
|
**Note:** Version bump only for package @ohif/extension-dicom-pdf
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@ohif/extension-dicom-pdf",
|
"name": "@ohif/extension-dicom-pdf",
|
||||||
"version": "1.0.6",
|
"version": "1.1.1",
|
||||||
"description": "OHIF extension for Dicom PDF",
|
"description": "OHIF extension for Dicom PDF",
|
||||||
"author": "OHIF",
|
"author": "OHIF",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
@@ -29,7 +29,7 @@
|
|||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@ohif/core": "^0.50.0",
|
"@ohif/core": "^0.50.0",
|
||||||
"dicom-parser": "^1.8.3",
|
"dicom-parser": "^1.8.11",
|
||||||
"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"
|
||||||
|
|||||||
@@ -3,6 +3,189 @@
|
|||||||
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.7.4](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-rt@0.7.3...@ohif/extension-dicom-rt@0.7.4) (2021-12-02)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-dicom-rt
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [0.7.3](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-rt@0.7.2...@ohif/extension-dicom-rt@0.7.3) (2021-10-26)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-dicom-rt
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [0.7.2](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-rt@0.7.1...@ohif/extension-dicom-rt@0.7.2) (2021-10-06)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-dicom-rt
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [0.7.1](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-rt@0.7.0...@ohif/extension-dicom-rt@0.7.1) (2021-09-28)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-dicom-rt
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# [0.7.0](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-rt@0.6.14...@ohif/extension-dicom-rt@0.7.0) (2021-09-23)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* Update to WADO Image Loader 4.0.3 to add dynamically imported WebAssembly codec support 🚀 ([#2537](https://github.com/OHIF/Viewers/issues/2537)) ([489d4b0](https://github.com/OHIF/Viewers/commit/489d4b0584f4657ceda1c5b9b68d5e9733827e47))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [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)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-dicom-rt
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [0.6.2](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-rt@0.6.1...@ohif/extension-dicom-rt@0.6.2) (2021-01-21)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-dicom-rt
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [0.6.1](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-rt@0.6.0...@ohif/extension-dicom-rt@0.6.1) (2020-12-10)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* panel issues and mpr button (IDC2122-IDC2117) ([32022f5](https://github.com/OHIF/Viewers/commit/32022f51f2d24f53f4c98188980db038a6dfe76c))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# [0.6.0](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-rt@0.5.0...@ohif/extension-dicom-rt@0.6.0) (2020-12-10)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* visualize overlapping segments in cornerstone ([#2185](https://github.com/OHIF/Viewers/issues/2185)) ([29fceac](https://github.com/OHIF/Viewers/commit/29fceacee97d51f1952a0f6b574c66596d32c201))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# [0.5.0](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-rt@0.4.9...@ohif/extension-dicom-rt@0.5.0) (2020-12-03)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* Update approach to better handle rtdose instead of disabling panel ([f3cecf1](https://github.com/OHIF/Viewers/commit/f3cecf129b16f8d24d239aa44fdff7f46e6ba683))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## [0.4.9](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-rt@0.4.8...@ohif/extension-dicom-rt@0.4.9) (2020-10-12)
|
## [0.4.9](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-rt@0.4.8...@ohif/extension-dicom-rt@0.4.9) (2020-10-12)
|
||||||
|
|
||||||
**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.4.9",
|
"version": "0.7.4",
|
||||||
"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",
|
||||||
@@ -29,9 +29,9 @@
|
|||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@ohif/core": "^0.50.0",
|
"@ohif/core": "^0.50.0",
|
||||||
"cornerstone-core": "^2.2.8",
|
"cornerstone-core": "^2.6.1",
|
||||||
"cornerstone-tools": "^4.20.1",
|
"cornerstone-tools": "^6.0.6",
|
||||||
"dcmjs": "0.16.5",
|
"dcmjs": "0.19.1",
|
||||||
"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",
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ const OHIFDicomRTStructSopClassHandler = {
|
|||||||
id: 'OHIFDicomRTStructSopClassHandler',
|
id: 'OHIFDicomRTStructSopClassHandler',
|
||||||
type: MODULE_TYPES.SOP_CLASS_HANDLER,
|
type: MODULE_TYPES.SOP_CLASS_HANDLER,
|
||||||
sopClassUIDs,
|
sopClassUIDs,
|
||||||
getDisplaySetFromSeries: function(
|
getDisplaySetFromSeries: function (
|
||||||
series,
|
series,
|
||||||
study,
|
study,
|
||||||
dicomWebClient,
|
dicomWebClient,
|
||||||
@@ -71,11 +71,11 @@ 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) {
|
||||||
return loadRTStruct(
|
return loadRTStruct(
|
||||||
rtStructDisplaySet,
|
rtStructDisplaySet,
|
||||||
referencedDisplaySet,
|
referencedDisplaySet,
|
||||||
|
|||||||
@@ -39,6 +39,8 @@
|
|||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.icons .angle-double-down, .icons .angle-double-up {
|
.icons .angle-double-down,
|
||||||
|
.icons .angle-double-up {
|
||||||
margin-left: 15px;
|
margin-left: 15px;
|
||||||
|
margin-right: -5px;
|
||||||
}
|
}
|
||||||
@@ -9,16 +9,21 @@
|
|||||||
|
|
||||||
.dcmrt-panel .dcmrt-panel-header {
|
.dcmrt-panel .dcmrt-panel-header {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: column;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
color: white;
|
color: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.dcmrt-panel .dcmrt-panel-header h3 {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.dcmrt-panel .dcmrt-panel-header .cog-icon {
|
.dcmrt-panel .dcmrt-panel-header .cog-icon {
|
||||||
align-self: flex-end;
|
align-self: flex-end;
|
||||||
color: var(--default-color);
|
color: var(--default-color);
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
margin-bottom: 20px;
|
min-height: 23px;
|
||||||
|
max-height: 23px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dcmrt-panel .dcmrt-panel-header .cog-icon:hover {
|
.dcmrt-panel .dcmrt-panel-header .cog-icon:hover {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState, useEffect, useCallback } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import cornerstoneTools from 'cornerstone-tools';
|
import cornerstoneTools from 'cornerstone-tools';
|
||||||
import cornerstone from 'cornerstone-core';
|
import cornerstone from 'cornerstone-core';
|
||||||
@@ -31,19 +31,20 @@ const refreshViewport = () => {
|
|||||||
*
|
*
|
||||||
* @param {Object} props
|
* @param {Object} props
|
||||||
* @param {Array} props.studies
|
* @param {Array} props.studies
|
||||||
* @param {Array} props.viewports - viewportSpecificData
|
* @param {Array} props.getActiveViewport - get active viewport data
|
||||||
* @param {number} props.activeIndex - activeViewportIndex
|
* @param {number} props.activeIndex - activeViewportIndex
|
||||||
* @param {number} props.isOpen - isOpen
|
* @param {number} props.isOpen - isOpen
|
||||||
* @returns component
|
* @returns component
|
||||||
*/
|
*/
|
||||||
const RTPanel = ({
|
const RTPanel = ({
|
||||||
studies,
|
studies,
|
||||||
viewports,
|
|
||||||
activeIndex,
|
activeIndex,
|
||||||
isOpen,
|
isOpen,
|
||||||
onContourItemClick,
|
onContourItemClick,
|
||||||
activeContexts = [],
|
activeContexts = [],
|
||||||
contexts = {},
|
contexts = {},
|
||||||
|
activeViewport,
|
||||||
|
getActiveViewport,
|
||||||
}) => {
|
}) => {
|
||||||
const isVTK = () => activeContexts.includes(contexts.VTK);
|
const isVTK = () => activeContexts.includes(contexts.VTK);
|
||||||
const isCornerstone = () => activeContexts.includes(contexts.CORNERSTONE);
|
const isCornerstone = () => activeContexts.includes(contexts.CORNERSTONE);
|
||||||
@@ -54,11 +55,11 @@ const RTPanel = ({
|
|||||||
referencedDisplaysets: [],
|
referencedDisplaysets: [],
|
||||||
sets: [],
|
sets: [],
|
||||||
selectedSet: null,
|
selectedSet: null,
|
||||||
|
isLocked: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
const [state, setState] = useState(DEFAULT_STATE);
|
const [state, setState] = useState(DEFAULT_STATE);
|
||||||
const [showSettings, setShowSettings] = useState(false);
|
const [showSettings, setShowSettings] = useState(false);
|
||||||
const activeViewport = viewports[activeIndex];
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* TODO: Improve the way we notify parts of the app that depends on rts to be loaded.
|
* TODO: Improve the way we notify parts of the app that depends on rts to be loaded.
|
||||||
@@ -78,6 +79,8 @@ const RTPanel = ({
|
|||||||
* without subscribing to external events.
|
* without subscribing to external events.
|
||||||
*/
|
*/
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
updateStructureSets();
|
||||||
|
|
||||||
document.addEventListener('extensiondicomrtrtloaded', updateStructureSets);
|
document.addEventListener('extensiondicomrtrtloaded', updateStructureSets);
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
@@ -88,20 +91,30 @@ const RTPanel = ({
|
|||||||
};
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const studyMetadata = studyMetadataManager.get(
|
||||||
|
activeViewport.StudyInstanceUID
|
||||||
|
);
|
||||||
|
const referencedDisplaysets = studyMetadata.getDerivedDatasets({
|
||||||
|
referencedSeriesInstanceUID: activeViewport.SeriesInstanceUID,
|
||||||
|
Modality: 'RTSTRUCT',
|
||||||
|
});
|
||||||
|
setState(state => ({ ...state, isLocked: !referencedDisplaysets.length }));
|
||||||
|
}, [activeViewport]);
|
||||||
|
|
||||||
const updateStructureSets = () => {
|
const updateStructureSets = () => {
|
||||||
|
const viewport = getActiveViewport();
|
||||||
const module = cornerstoneTools.getModule('rtstruct');
|
const module = cornerstoneTools.getModule('rtstruct');
|
||||||
const StructureSets = module.state.StructureSets;
|
const StructureSets = module.state.StructureSets;
|
||||||
|
|
||||||
if (StructureSets && StructureSets.length) {
|
if (StructureSets && StructureSets.length) {
|
||||||
const viewportSets = module.getters.structuresSetsWhichReferenceSeriesInstanceUid(
|
const viewportSets = module.getters.structuresSetsWhichReferenceSeriesInstanceUid(
|
||||||
activeViewport.SeriesInstanceUID
|
viewport.SeriesInstanceUID
|
||||||
);
|
);
|
||||||
|
|
||||||
const studyMetadata = studyMetadataManager.get(
|
const studyMetadata = studyMetadataManager.get(viewport.StudyInstanceUID);
|
||||||
activeViewport.StudyInstanceUID
|
|
||||||
);
|
|
||||||
const referencedDisplaysets = studyMetadata.getDerivedDatasets({
|
const referencedDisplaysets = studyMetadata.getDerivedDatasets({
|
||||||
referencedSeriesInstanceUID: activeViewport.SeriesInstanceUID,
|
referencedSeriesInstanceUID: viewport.SeriesInstanceUID,
|
||||||
Modality: 'RTSTRUCT',
|
Modality: 'RTSTRUCT',
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -118,10 +131,6 @@ const RTPanel = ({
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
updateStructureSets();
|
|
||||||
}, [studies, viewports, activeIndex]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setShowSettings(showSettings && !isOpen);
|
setShowSettings(showSettings && !isOpen);
|
||||||
}, [isOpen]);
|
}, [isOpen]);
|
||||||
@@ -231,16 +240,19 @@ const RTPanel = ({
|
|||||||
return (
|
return (
|
||||||
<div className="dcmrt-panel">
|
<div className="dcmrt-panel">
|
||||||
<div className="dcmrt-panel-header">
|
<div className="dcmrt-panel-header">
|
||||||
|
{' '}
|
||||||
|
{!state.isLocked && (
|
||||||
|
<Icon
|
||||||
|
className="cog-icon"
|
||||||
|
name="cog"
|
||||||
|
width="25px"
|
||||||
|
height="25px"
|
||||||
|
onClick={() => setShowSettings(true)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
<h3>RT Structure Sets</h3>
|
<h3>RT Structure Sets</h3>
|
||||||
<Icon
|
|
||||||
className="cog-icon"
|
|
||||||
name="cog"
|
|
||||||
width="25px"
|
|
||||||
height="25px"
|
|
||||||
onClick={() => setShowSettings(true)}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
{!state.referencedDisplaysets.length && (
|
{!state.isLocked && !state.referencedDisplaysets.length && (
|
||||||
<LoadingIndicator expand height="70px" width="70px" />
|
<LoadingIndicator expand height="70px" width="70px" />
|
||||||
)}
|
)}
|
||||||
{state.sets &&
|
{state.sets &&
|
||||||
@@ -249,12 +261,15 @@ const RTPanel = ({
|
|||||||
|
|
||||||
const module = cornerstoneTools.getModule('rtstruct');
|
const module = cornerstoneTools.getModule('rtstruct');
|
||||||
const sets = module.getters.structuresSetsWhichReferenceSeriesInstanceUid(
|
const sets = module.getters.structuresSetsWhichReferenceSeriesInstanceUid(
|
||||||
viewports[activeIndex].SeriesInstanceUID
|
activeViewport.SeriesInstanceUID
|
||||||
);
|
);
|
||||||
|
|
||||||
const loadedSet = sets.find(
|
const loadedSet = sets.find(
|
||||||
set => set.SeriesInstanceUID === SeriesInstanceUID
|
set => set.SeriesInstanceUID === SeriesInstanceUID
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (!loadedSet) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PanelSection
|
<PanelSection
|
||||||
key={SeriesInstanceUID}
|
key={SeriesInstanceUID}
|
||||||
@@ -276,17 +291,17 @@ const RTPanel = ({
|
|||||||
module.setters.hideStructureSet(loadedSet.SeriesInstanceUID);
|
module.setters.hideStructureSet(loadedSet.SeriesInstanceUID);
|
||||||
}
|
}
|
||||||
const sets = module.getters.structuresSetsWhichReferenceSeriesInstanceUid(
|
const sets = module.getters.structuresSetsWhichReferenceSeriesInstanceUid(
|
||||||
viewports[activeIndex].SeriesInstanceUID
|
activeViewport.SeriesInstanceUID
|
||||||
);
|
);
|
||||||
setState(state => ({ ...state, sets }));
|
setState(state => ({ ...state, sets }));
|
||||||
refreshViewport();
|
refreshViewport();
|
||||||
}}
|
}}
|
||||||
onExpandChange={async () => {
|
onExpandChange={async () => {
|
||||||
if (!isLoaded) {
|
if (!isLoaded) {
|
||||||
await displaySet.load(viewports[activeIndex], studies);
|
await displaySet.load(activeViewport, studies);
|
||||||
const module = cornerstoneTools.getModule('rtstruct');
|
const module = cornerstoneTools.getModule('rtstruct');
|
||||||
const sets = module.getters.structuresSetsWhichReferenceSeriesInstanceUid(
|
const sets = module.getters.structuresSetsWhichReferenceSeriesInstanceUid(
|
||||||
viewports[activeIndex].SeriesInstanceUID
|
activeViewport.SeriesInstanceUID
|
||||||
);
|
);
|
||||||
const selectedSet = sets.find(
|
const selectedSet = sets.find(
|
||||||
set => set.SeriesInstanceUID === SeriesInstanceUID
|
set => set.SeriesInstanceUID === SeriesInstanceUID
|
||||||
|
|||||||
@@ -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,10 +1,14 @@
|
|||||||
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';
|
||||||
|
|
||||||
|
const { studyMetadataManager } = utils;
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
/**
|
/**
|
||||||
* Only required property. Should be a unique value across all extensions.
|
* Only required property. Should be a unique value across all extensions.
|
||||||
@@ -39,30 +43,75 @@ 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',
|
||||||
isDisabled: studies => {
|
stateEvent: RTPanelTabChangedEvent,
|
||||||
|
isDisabled: (studies, activeViewport) => {
|
||||||
if (!studies) {
|
if (!studies) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let i = 0; i < studies.length; i++) {
|
if (activeViewport) {
|
||||||
const study = studies[i];
|
const studyMetadata = studyMetadataManager.get(
|
||||||
|
activeViewport.StudyInstanceUID
|
||||||
if (study && study.series) {
|
);
|
||||||
for (let j = 0; j < study.series.length; j++) {
|
if (!studyMetadata) {
|
||||||
const series = study.series[j];
|
return;
|
||||||
if (
|
}
|
||||||
/* Could be expanded to contain RTPLAN and RTDOSE information in the future */
|
const referencedDisplaySets = studyMetadata.getDerivedDatasets({
|
||||||
['RTSTRUCT'].includes(series.Modality)
|
referencedSeriesInstanceUID: activeViewport.SeriesInstanceUID,
|
||||||
) {
|
Modality: 'RTSTRUCT',
|
||||||
return false;
|
});
|
||||||
}
|
if (
|
||||||
}
|
referencedDisplaySets &&
|
||||||
|
referencedDisplaySets.some(ds =>
|
||||||
|
['RTSTRUCT'].includes(ds.Modality)
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
triggerRTPanelUpdatedEvent({
|
||||||
|
badgeNumber: referencedDisplaySets.length,
|
||||||
|
target: 'rt-panel',
|
||||||
|
});
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -96,6 +96,11 @@ export default async function loadRTStruct(
|
|||||||
imageIdSopInstanceUidPairs
|
imageIdSopInstanceUidPairs
|
||||||
);
|
);
|
||||||
const imageId = _getImageId(imageIdSopInstanceUidPairs, sopInstanceUID);
|
const imageId = _getImageId(imageIdSopInstanceUidPairs, sopInstanceUID);
|
||||||
|
|
||||||
|
if (!imageId) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
const imageIdSpecificToolData = _getOrCreateImageIdSpecificToolData(
|
const imageIdSpecificToolData = _getOrCreateImageIdSpecificToolData(
|
||||||
toolState,
|
toolState,
|
||||||
imageId,
|
imageId,
|
||||||
@@ -166,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);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -294,7 +305,9 @@ const _getImageId = (imageIdSopInstanceUidPairs, sopInstanceUID) => {
|
|||||||
imageIdSopInstanceUidPairsEntry.sopInstanceUID === sopInstanceUID
|
imageIdSopInstanceUidPairsEntry.sopInstanceUID === sopInstanceUID
|
||||||
);
|
);
|
||||||
|
|
||||||
return imageIdSopInstanceUidPairsEntry.imageId;
|
return imageIdSopInstanceUidPairsEntry
|
||||||
|
? imageIdSopInstanceUidPairsEntry.imageId
|
||||||
|
: null;
|
||||||
};
|
};
|
||||||
|
|
||||||
function _getImageIdSopInstanceUidPairsForDisplaySet(
|
function _getImageIdSopInstanceUidPairsForDisplaySet(
|
||||||
|
|||||||
@@ -3,6 +3,202 @@
|
|||||||
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.7.5](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-segmentation@0.7.4...@ohif/extension-dicom-segmentation@0.7.5) (2021-12-02)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-dicom-segmentation
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [0.7.4](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-segmentation@0.7.3...@ohif/extension-dicom-segmentation@0.7.4) (2021-10-27)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-dicom-segmentation
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [0.7.3](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-segmentation@0.7.2...@ohif/extension-dicom-segmentation@0.7.3) (2021-10-26)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-dicom-segmentation
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [0.7.2](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-segmentation@0.7.1...@ohif/extension-dicom-segmentation@0.7.2) (2021-10-06)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-dicom-segmentation
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [0.7.1](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-segmentation@0.7.0...@ohif/extension-dicom-segmentation@0.7.1) (2021-09-28)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-dicom-segmentation
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# [0.7.0](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-segmentation@0.6.11...@ohif/extension-dicom-segmentation@0.7.0) (2021-09-23)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* Update to WADO Image Loader 4.0.3 to add dynamically imported WebAssembly codec support 🚀 ([#2537](https://github.com/OHIF/Viewers/issues/2537)) ([489d4b0](https://github.com/OHIF/Viewers/commit/489d4b0584f4657ceda1c5b9b68d5e9733827e47))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [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)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-dicom-segmentation
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# [0.5.0](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-segmentation@0.4.7...@ohif/extension-dicom-segmentation@0.5.0) (2020-12-10)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* visualize overlapping segments in cornerstone ([#2185](https://github.com/OHIF/Viewers/issues/2185)) ([29fceac](https://github.com/OHIF/Viewers/commit/29fceacee97d51f1952a0f6b574c66596d32c201))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## [0.4.7](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-segmentation@0.4.6...@ohif/extension-dicom-segmentation@0.4.7) (2020-10-07)
|
## [0.4.7](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-segmentation@0.4.6...@ohif/extension-dicom-segmentation@0.4.7) (2020-10-07)
|
||||||
|
|
||||||
**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.4.7",
|
"version": "0.7.5",
|
||||||
"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",
|
||||||
@@ -29,9 +29,9 @@
|
|||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@ohif/core": "^0.50.0",
|
"@ohif/core": "^0.50.0",
|
||||||
"cornerstone-core": "^2.2.8",
|
"cornerstone-core": "^2.6.1",
|
||||||
"cornerstone-tools": "^4.20.1",
|
"cornerstone-tools": "^6.0.6",
|
||||||
"dcmjs": "0.16.5",
|
"dcmjs": "0.19.1",
|
||||||
"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"
|
||||||
|
|||||||
@@ -24,7 +24,8 @@ const SegmentItem = ({
|
|||||||
onClick,
|
onClick,
|
||||||
itemClass,
|
itemClass,
|
||||||
color,
|
color,
|
||||||
visible = true,
|
labelmap3D,
|
||||||
|
visible,
|
||||||
onVisibilityChange,
|
onVisibilityChange,
|
||||||
}) => {
|
}) => {
|
||||||
const [isVisible, setIsVisible] = useState(visible);
|
const [isVisible, setIsVisible] = useState(visible);
|
||||||
@@ -33,6 +34,15 @@ const SegmentItem = ({
|
|||||||
setIsVisible(visible);
|
setIsVisible(visible);
|
||||||
}, [visible]);
|
}, [visible]);
|
||||||
|
|
||||||
|
const onClickHandler = () => onClick(index);
|
||||||
|
|
||||||
|
const onVisibilityChangeHandler = event => {
|
||||||
|
event.stopPropagation();
|
||||||
|
const newVisibility = !isVisible;
|
||||||
|
setIsVisible(newVisibility);
|
||||||
|
onVisibilityChange(newVisibility, index, labelmap3D);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="dcmseg-segment-item">
|
<div className="dcmseg-segment-item">
|
||||||
<TableListItem
|
<TableListItem
|
||||||
@@ -42,7 +52,7 @@ const SegmentItem = ({
|
|||||||
itemClass={itemClass}
|
itemClass={itemClass}
|
||||||
itemMeta={<ColoredCircle color={color} />}
|
itemMeta={<ColoredCircle color={color} />}
|
||||||
itemMetaClass="segment-color-section"
|
itemMetaClass="segment-color-section"
|
||||||
onItemClick={onClick}
|
onItemClick={onClickHandler}
|
||||||
>
|
>
|
||||||
<div>
|
<div>
|
||||||
<div className="segment-label" style={{ marginBottom: 4 }}>
|
<div className="segment-label" style={{ marginBottom: 4 }}>
|
||||||
@@ -63,12 +73,7 @@ const SegmentItem = ({
|
|||||||
name={isVisible ? 'eye' : 'eye-closed'}
|
name={isVisible ? 'eye' : 'eye-closed'}
|
||||||
width="20px"
|
width="20px"
|
||||||
height="20px"
|
height="20px"
|
||||||
onClick={event => {
|
onClick={onVisibilityChangeHandler}
|
||||||
event.stopPropagation();
|
|
||||||
const newVisibility = !isVisible;
|
|
||||||
setIsVisible(newVisibility);
|
|
||||||
onVisibilityChange(newVisibility);
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{false && <div className="segment-info">{'...'}</div>}
|
{false && <div className="segment-info">{'...'}</div>}
|
||||||
|
|||||||
+64
-5
@@ -6,6 +6,7 @@
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
padding: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dcmseg-segmentation-panel.disabled {
|
.dcmseg-segmentation-panel.disabled {
|
||||||
@@ -14,19 +15,19 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.dcmseg-segmentation-panel h3 {
|
.dcmseg-segmentation-panel h3 {
|
||||||
padding-left: 20px;
|
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dcmseg-segmentation-panel .segmentations {
|
.dcmseg-segmentation-panel .segmentations {
|
||||||
padding: 0 20px 20px 20px;
|
padding-bottom: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dcmseg-segmentation-panel .cog-icon {
|
.dcmseg-segmentation-panel .cog-icon {
|
||||||
margin: 20px 20px 0 0;
|
|
||||||
align-self: flex-end;
|
align-self: flex-end;
|
||||||
color: var(--default-color);
|
color: var(--default-color);
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
min-height: 23px;
|
||||||
|
max-height: 23px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dcmseg-segmentation-panel .cog-icon:hover {
|
.dcmseg-segmentation-panel .cog-icon:hover {
|
||||||
@@ -48,11 +49,11 @@
|
|||||||
height: 150px;
|
height: 150px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dcmseg-segmentation-panel .selector-form>div {
|
.dcmseg-segmentation-panel .selector-form > div {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dcmseg-segmentation-panel .selector-form>div:first-of-type {
|
.dcmseg-segmentation-panel .selector-form > div:first-of-type {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
@@ -64,3 +65,61 @@
|
|||||||
flex: unset;
|
flex: unset;
|
||||||
margin-right: 16px;
|
margin-right: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.SegmentsSection {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
color: var(--text-primary-color);
|
||||||
|
font-size: 12px;
|
||||||
|
margin-bottom: 0px;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.SegmentsSection .header {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
background-color: var(--ui-gray-dark);
|
||||||
|
border-radius: 5px;
|
||||||
|
font-weight: bold;
|
||||||
|
padding: 10px 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.SegmentsSection .count {
|
||||||
|
font-size: 15px;
|
||||||
|
color: var(--active-color);
|
||||||
|
margin-left: 15px;
|
||||||
|
border-radius: 5px;
|
||||||
|
background-color: var(--ui-gray-darker);
|
||||||
|
text-align: center;
|
||||||
|
min-width: 25px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.SegmentsSection .header .eye-icon,
|
||||||
|
.SegmentsSection .header .angle-double-down,
|
||||||
|
.SegmentsSection .header .angle-double-up {
|
||||||
|
cursor: pointer;
|
||||||
|
color: var(--active-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.SegmentsSection .header .eye-icon:hover,
|
||||||
|
.SegmentsSection .header .angle-double-down:hover,
|
||||||
|
.SegmentsSection .header .angle-double-up:hover {
|
||||||
|
color: var(--hover-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.SegmentsSection .header .eye-icon.expanded {
|
||||||
|
color: var(--default-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.SegmentsSection .header .icons {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.SegmentsSection .header .icons .angle-double-down,
|
||||||
|
.SegmentsSection .header .icons .angle-double-up {
|
||||||
|
margin-left: 15px;
|
||||||
|
}
|
||||||
+505
-353
File diff suppressed because it is too large.
Load diff
@@ -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}
|
||||||
|
|||||||
@@ -1,6 +1,11 @@
|
|||||||
import { MODULE_TYPES, utils } from '@ohif/core';
|
import { MODULE_TYPES, utils } from '@ohif/core';
|
||||||
import loadSegmentation from './loadSegmentation';
|
import loadSegmentation from './loadSegmentation';
|
||||||
import getSourceDisplaySet from './getSourceDisplaySet';
|
import getSourceDisplaySet from './getSourceDisplaySet';
|
||||||
|
import OHIF from '@ohif/core';
|
||||||
|
import dcmjs from 'dcmjs';
|
||||||
|
|
||||||
|
const { DicomLoaderService } = OHIF.utils;
|
||||||
|
const { DicomMessage, DicomMetaDictionary } = dcmjs.data;
|
||||||
|
|
||||||
// TODO: Should probably use dcmjs for this
|
// TODO: Should probably use dcmjs for this
|
||||||
const SOP_CLASS_UIDS = {
|
const SOP_CLASS_UIDS = {
|
||||||
@@ -46,11 +51,12 @@ export default function getSopClassHandlerModule({ servicesManager }) {
|
|||||||
StudyInstanceUID,
|
StudyInstanceUID,
|
||||||
FrameOfReferenceUID,
|
FrameOfReferenceUID,
|
||||||
authorizationHeaders,
|
authorizationHeaders,
|
||||||
metadata,
|
|
||||||
isDerived: true,
|
isDerived: true,
|
||||||
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,
|
||||||
SeriesDate,
|
SeriesDate,
|
||||||
SeriesTime,
|
SeriesTime,
|
||||||
SeriesNumber,
|
SeriesNumber,
|
||||||
@@ -58,15 +64,122 @@ export default function getSopClassHandlerModule({ servicesManager }) {
|
|||||||
metadata,
|
metadata,
|
||||||
};
|
};
|
||||||
|
|
||||||
segDisplaySet.getSourceDisplaySet = function(studies) {
|
segDisplaySet.getSourceDisplaySet = function(
|
||||||
return getSourceDisplaySet(studies, segDisplaySet);
|
studies,
|
||||||
|
activateLabelMap = true,
|
||||||
|
onDisplaySetLoadFailureHandler
|
||||||
|
) {
|
||||||
|
return getSourceDisplaySet(
|
||||||
|
studies,
|
||||||
|
segDisplaySet,
|
||||||
|
activateLabelMap,
|
||||||
|
onDisplaySetLoadFailureHandler
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
segDisplaySet.load = function(referencedDisplaySet, studies) {
|
segDisplaySet.load = async function(referencedDisplaySet, studies) {
|
||||||
return loadSegmentation(segDisplaySet, referencedDisplaySet, studies);
|
segDisplaySet.isLoaded = true;
|
||||||
|
const { StudyInstanceUID } = referencedDisplaySet;
|
||||||
|
const segArrayBuffer = await DicomLoaderService.findDicomDataPromise(
|
||||||
|
segDisplaySet,
|
||||||
|
studies
|
||||||
|
);
|
||||||
|
const dicomData = DicomMessage.readFile(segArrayBuffer);
|
||||||
|
const dataset = DicomMetaDictionary.naturalizeDataset(dicomData.dict);
|
||||||
|
dataset._meta = DicomMetaDictionary.namifyDataset(dicomData.meta);
|
||||||
|
const imageIds = _getImageIdsForDisplaySet(
|
||||||
|
studies,
|
||||||
|
StudyInstanceUID,
|
||||||
|
referencedDisplaySet.SeriesInstanceUID
|
||||||
|
);
|
||||||
|
|
||||||
|
const results = await _parseSeg(segArrayBuffer, imageIds);
|
||||||
|
if (results === undefined) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const {
|
||||||
|
labelmapBufferArray,
|
||||||
|
segMetadata,
|
||||||
|
segmentsOnFrame,
|
||||||
|
segmentsOnFrameArray,
|
||||||
|
} = results;
|
||||||
|
let labelmapIndex;
|
||||||
|
if (labelmapBufferArray.length > 1) {
|
||||||
|
let labelmapIndexes = [];
|
||||||
|
for (let i = 0; i < labelmapBufferArray.length; ++i) {
|
||||||
|
segMetadata.segmentationSeriesInstanceUID =
|
||||||
|
segDisplaySet.SeriesInstanceUID;
|
||||||
|
segMetadata.hasOverlapping = true;
|
||||||
|
labelmapIndexes.push(
|
||||||
|
await loadSegmentation(
|
||||||
|
imageIds,
|
||||||
|
segDisplaySet,
|
||||||
|
labelmapBufferArray[i],
|
||||||
|
segMetadata,
|
||||||
|
segmentsOnFrame,
|
||||||
|
segmentsOnFrameArray[i]
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 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 {
|
||||||
|
segMetadata.segmentationSeriesInstanceUID =
|
||||||
|
segDisplaySet.SeriesInstanceUID;
|
||||||
|
segMetadata.hasOverlapping = false;
|
||||||
|
labelmapIndex = await loadSegmentation(
|
||||||
|
imageIds,
|
||||||
|
segDisplaySet,
|
||||||
|
labelmapBufferArray[0],
|
||||||
|
segMetadata,
|
||||||
|
segmentsOnFrame,
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return segDisplaySet;
|
return segDisplaySet;
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function _parseSeg(arrayBuffer, imageIds) {
|
||||||
|
return dcmjs.adapters.Cornerstone.Segmentation.generateToolState(
|
||||||
|
imageIds,
|
||||||
|
arrayBuffer,
|
||||||
|
cornerstone.metaData
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function _getImageIdsForDisplaySet(
|
||||||
|
studies,
|
||||||
|
StudyInstanceUID,
|
||||||
|
SeriesInstanceUID
|
||||||
|
) {
|
||||||
|
const study = studies.find(
|
||||||
|
study => study.StudyInstanceUID === StudyInstanceUID
|
||||||
|
);
|
||||||
|
|
||||||
|
const displaySets = study.displaySets.filter(displaySet => {
|
||||||
|
return displaySet.SeriesInstanceUID === SeriesInstanceUID;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (displaySets.length > 1) {
|
||||||
|
console.warn(
|
||||||
|
'More than one display set with the same SeriesInstanceUID. This is not supported yet...'
|
||||||
|
);
|
||||||
|
// TODO -> We could make check the instance list and see if any match?
|
||||||
|
// Do we split the segmentation into two cornerstoneTools segmentations if there are images in both series?
|
||||||
|
// ^ Will that even happen?
|
||||||
|
}
|
||||||
|
|
||||||
|
const referencedDisplaySet = displaySets[0];
|
||||||
|
|
||||||
|
return referencedDisplaySet.images.map(image => image.getImageId());
|
||||||
|
}
|
||||||
@@ -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,13 +82,51 @@ 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',
|
||||||
isDisabled: studies => {
|
stateEvent: SegmentationPanelTabUpdatedEvent,
|
||||||
|
isDisabled: (studies, activeViewport) => {
|
||||||
if (!studies) {
|
if (!studies) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -97,6 +139,23 @@ export default {
|
|||||||
const series = study.series[j];
|
const series = study.series[j];
|
||||||
|
|
||||||
if (series.Modality === 'SEG') {
|
if (series.Modality === 'SEG') {
|
||||||
|
if (activeViewport) {
|
||||||
|
const studyMetadata = studyMetadataManager.get(
|
||||||
|
activeViewport.StudyInstanceUID
|
||||||
|
);
|
||||||
|
if (!studyMetadata) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const referencedDS = studyMetadata.getDerivedDatasets({
|
||||||
|
referencedSeriesInstanceUID:
|
||||||
|
activeViewport.SeriesInstanceUID,
|
||||||
|
Modality: 'SEG',
|
||||||
|
});
|
||||||
|
triggerSegmentationPanelTabUpdatedEvent({
|
||||||
|
badgeNumber: referencedDS.length,
|
||||||
|
target: 'segmentation-panel',
|
||||||
|
});
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,92 +1,77 @@
|
|||||||
import OHIF from '@ohif/core';
|
|
||||||
import dcmjs from 'dcmjs';
|
import dcmjs from 'dcmjs';
|
||||||
import cornerstone from 'cornerstone-core';
|
import cornerstone from 'cornerstone-core';
|
||||||
import cornerstoneTools from 'cornerstone-tools';
|
import cornerstoneTools from 'cornerstone-tools';
|
||||||
|
|
||||||
const { DicomLoaderService } = OHIF.utils;
|
|
||||||
|
|
||||||
export default async function loadSegmentation(
|
export default async function loadSegmentation(
|
||||||
|
imageIds,
|
||||||
segDisplaySet,
|
segDisplaySet,
|
||||||
referencedDisplaySet,
|
labelmapBuffer,
|
||||||
studies
|
segMetadata,
|
||||||
|
segmentsOnFrame,
|
||||||
|
labelmapSegments
|
||||||
) {
|
) {
|
||||||
const { StudyInstanceUID } = referencedDisplaySet;
|
const { setters } = cornerstoneTools.getModule('segmentation');
|
||||||
|
|
||||||
// Set here is loading is asynchronous.
|
// TODO: Could define a color LUT based on colors in the SEG.
|
||||||
// If this function throws its set back to false.
|
const labelmapIndex = _getNextLabelmapIndex(imageIds[0]);
|
||||||
segDisplaySet.isLoaded = true;
|
const colorLUTIndex = _makeColorLUTAndGetIndex(segMetadata);
|
||||||
|
|
||||||
const segArrayBuffer = await DicomLoaderService.findDicomDataPromise(
|
setters.labelmap3DByFirstImageId(
|
||||||
segDisplaySet,
|
imageIds[0],
|
||||||
studies
|
labelmapBuffer,
|
||||||
|
labelmapIndex,
|
||||||
|
segMetadata,
|
||||||
|
imageIds.length,
|
||||||
|
segmentsOnFrame,
|
||||||
|
colorLUTIndex
|
||||||
);
|
);
|
||||||
|
|
||||||
const dicomData = dcmjs.data.DicomMessage.readFile(segArrayBuffer);
|
if (!segDisplaySet.labelmapSegments) {
|
||||||
const dataset = dcmjs.data.DicomMetaDictionary.naturalizeDataset(
|
segDisplaySet.labelmapSegments = {};
|
||||||
dicomData.dict
|
}
|
||||||
);
|
|
||||||
|
|
||||||
dataset._meta = dcmjs.data.DicomMetaDictionary.namifyDataset(dicomData.meta);
|
/**
|
||||||
|
* Cache each labelmap segments.
|
||||||
|
* This data is used to determine the active label map when a given segment is activated/clicked.
|
||||||
|
*/
|
||||||
|
segDisplaySet.labelmapSegments[labelmapIndex] = labelmapSegments.length
|
||||||
|
? Array.from(
|
||||||
|
new Set(labelmapSegments.filter(a => !!a).reduce((a, b) => a.concat(b)))
|
||||||
|
)
|
||||||
|
: [];
|
||||||
|
segDisplaySet.labelmapIndex = labelmapIndex;
|
||||||
|
|
||||||
const imageIds = _getImageIdsForDisplaySet(
|
/*
|
||||||
studies,
|
* TODO: Improve the way we notify parts of the app that depends on segs to be loaded.
|
||||||
StudyInstanceUID,
|
*
|
||||||
referencedDisplaySet.SeriesInstanceUID
|
* Currently we are using a non-ideal implementation through a custom event to notify the segmentation panel
|
||||||
);
|
* or other components that could rely on loaded segmentations that
|
||||||
|
* the segments were loaded so that e.g. when the user opens the panel
|
||||||
return new Promise((resolve, reject) => {
|
* before the segments are fully loaded, the panel can subscribe to this custom event
|
||||||
let results;
|
* and update itself with the new segments.
|
||||||
|
*
|
||||||
try {
|
* This limitation is due to the fact that the cs segmentation module is an object (which will be
|
||||||
results = _parseSeg(segArrayBuffer, imageIds);
|
* updated after the segments are loaded) that React its not aware of its changes
|
||||||
} catch (error) {
|
* because the module object its not passed in to the panel component as prop but accessed externally.
|
||||||
segDisplaySet.isLoaded = false;
|
*
|
||||||
segDisplaySet.loadError = true;
|
* Improving this event approach to something reactive that can be tracked inside the react lifecycle,
|
||||||
reject(error);
|
* allows us to easily watch the module or the segmentations loading process in any other component
|
||||||
}
|
* without subscribing to external events.
|
||||||
|
*/
|
||||||
const { labelmapBuffer, segMetadata, segmentsOnFrame } = results;
|
console.log('Segmentation loaded.');
|
||||||
const { setters } = cornerstoneTools.getModule('segmentation');
|
const event = new CustomEvent('extensiondicomsegmentationsegloaded', {
|
||||||
|
detail: {
|
||||||
// TODO: Could define a color LUT based on colors in the SEG.
|
imageIds,
|
||||||
const labelmapIndex = _getNextLabelmapIndex(imageIds[0]);
|
segDisplaySet,
|
||||||
const colorLUTIndex = _makeColorLUTAndGetIndex(segMetadata);
|
|
||||||
|
|
||||||
setters.labelmap3DByFirstImageId(
|
|
||||||
imageIds[0],
|
|
||||||
labelmapBuffer,
|
labelmapBuffer,
|
||||||
labelmapIndex,
|
|
||||||
segMetadata,
|
segMetadata,
|
||||||
imageIds.length,
|
|
||||||
segmentsOnFrame,
|
segmentsOnFrame,
|
||||||
colorLUTIndex
|
labelmapSegments,
|
||||||
);
|
},
|
||||||
|
|
||||||
segDisplaySet.labelmapIndex = labelmapIndex;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* TODO: Improve the way we notify parts of the app that depends on segs to be loaded.
|
|
||||||
*
|
|
||||||
* Currently we are using a non-ideal implementation through a custom event to notify the segmentation panel
|
|
||||||
* or other components that could rely on loaded segmentations that
|
|
||||||
* the segments were loaded so that e.g. when the user opens the panel
|
|
||||||
* before the segments are fully loaded, the panel can subscribe to this custom event
|
|
||||||
* and update itself with the new segments.
|
|
||||||
*
|
|
||||||
* This limitation is due to the fact that the cs segmentation module is an object (which will be
|
|
||||||
* updated after the segments are loaded) that React its not aware of its changes
|
|
||||||
* because the module object its not passed in to the panel component as prop but accessed externally.
|
|
||||||
*
|
|
||||||
* Improving this event approach to something reactive that can be tracked inside the react lifecycle,
|
|
||||||
* allows us to easily watch the module or the segmentations loading process in any other component
|
|
||||||
* without subscribing to external events.
|
|
||||||
*/
|
|
||||||
console.log('Segmentation loaded.');
|
|
||||||
const event = new CustomEvent('extensiondicomsegmentationsegloaded');
|
|
||||||
document.dispatchEvent(event);
|
|
||||||
|
|
||||||
resolve(labelmapIndex);
|
|
||||||
});
|
});
|
||||||
|
document.dispatchEvent(event);
|
||||||
|
|
||||||
|
return labelmapIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
function _getNextLabelmapIndex(firstImageId) {
|
function _getNextLabelmapIndex(firstImageId) {
|
||||||
@@ -172,38 +157,3 @@ function _getNextColorLUTIndex() {
|
|||||||
|
|
||||||
return colorLUTIndex;
|
return colorLUTIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
function _parseSeg(arrayBuffer, imageIds) {
|
|
||||||
return dcmjs.adapters.Cornerstone.Segmentation.generateToolState(
|
|
||||||
imageIds,
|
|
||||||
arrayBuffer,
|
|
||||||
cornerstone.metaData
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function _getImageIdsForDisplaySet(
|
|
||||||
studies,
|
|
||||||
StudyInstanceUID,
|
|
||||||
SeriesInstanceUID
|
|
||||||
) {
|
|
||||||
const study = studies.find(
|
|
||||||
study => study.StudyInstanceUID === StudyInstanceUID
|
|
||||||
);
|
|
||||||
|
|
||||||
const displaySets = study.displaySets.filter(displaySet => {
|
|
||||||
return displaySet.SeriesInstanceUID === SeriesInstanceUID;
|
|
||||||
});
|
|
||||||
|
|
||||||
if (displaySets.length > 1) {
|
|
||||||
console.warn(
|
|
||||||
'More than one display set with the same SeriesInstanceUID. This is not supported yet...'
|
|
||||||
);
|
|
||||||
// TODO -> We could make check the instance list and see if any match?
|
|
||||||
// Do we split the segmentation into two cornerstoneTools segmentations if there are images in both series?
|
|
||||||
// ^ Will that even happen?
|
|
||||||
}
|
|
||||||
|
|
||||||
const referencedDisplaySet = displaySets[0];
|
|
||||||
|
|
||||||
return referencedDisplaySet.images.map(image => image.getImageId());
|
|
||||||
}
|
|
||||||
@@ -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(
|
||||||
@@ -37,31 +37,43 @@ export default async function setActiveLabelmap(
|
|||||||
? brushStackState.activeLabelmapIndex
|
? brushStackState.activeLabelmapIndex
|
||||||
: undefined;
|
: undefined;
|
||||||
|
|
||||||
if (displaySet.labelmapIndex === activeLabelmapIndex) {
|
let labelmapIndex =
|
||||||
|
displaySet.hasOverlapping === true
|
||||||
|
? displaySet.originLabelMapIndex
|
||||||
|
: displaySet.labelmapIndex;
|
||||||
|
|
||||||
|
if (labelmapIndex === activeLabelmapIndex) {
|
||||||
log.warn(`${activeLabelmapIndex} is already the active labelmap`);
|
log.warn(`${activeLabelmapIndex} is already the active labelmap`);
|
||||||
return displaySet.labelmapIndex;
|
return labelmapIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
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 =
|
||||||
|
displaySet.hasOverlapping === true
|
||||||
|
? displaySet.originLabelMapIndex
|
||||||
|
: displaySet.labelmapIndex;
|
||||||
|
|
||||||
// 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 = displaySet.labelmapIndex;
|
if (brushStackState) {
|
||||||
|
brushStackState.activeLabelmapIndex = labelmapIndex;
|
||||||
|
}
|
||||||
|
|
||||||
refreshViewports();
|
refreshViewports();
|
||||||
callback();
|
callback();
|
||||||
|
|
||||||
return displaySet.labelmapIndex;
|
return labelmapIndex;
|
||||||
}
|
}
|
||||||
@@ -3,6 +3,170 @@
|
|||||||
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.15](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-tag-browser@0.2.14...@ohif/extension-dicom-tag-browser@0.2.15) (2021-12-02)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-dicom-tag-browser
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [0.2.14](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-tag-browser@0.2.13...@ohif/extension-dicom-tag-browser@0.2.14) (2021-11-30)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-dicom-tag-browser
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [0.2.13](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-tag-browser@0.2.12...@ohif/extension-dicom-tag-browser@0.2.13) (2021-10-26)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-dicom-tag-browser
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [0.2.12](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-tag-browser@0.2.11...@ohif/extension-dicom-tag-browser@0.2.12) (2021-09-28)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-dicom-tag-browser
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [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)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* visualize overlapping segments in cornerstone ([#2185](https://github.com/OHIF/Viewers/issues/2185)) ([29fceac](https://github.com/OHIF/Viewers/commit/29fceacee97d51f1952a0f6b574c66596d32c201))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [0.1.1](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-tag-browser@0.1.0...@ohif/extension-dicom-tag-browser@0.1.1) (2020-12-03)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* fallback to original dataset VR for XS ([f57725a](https://github.com/OHIF/Viewers/commit/f57725ac8bfd59ea46ab334a1823882afadefba1))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# [0.1.0](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-tag-browser@0.0.5...@ohif/extension-dicom-tag-browser@0.1.0) (2020-12-03)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* add show scroll option to modal ([be494a7](https://github.com/OHIF/Viewers/commit/be494a7376d15777dbe598289c4ecdb9f48a6a48))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [0.0.5](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-tag-browser@0.0.4...@ohif/extension-dicom-tag-browser@0.0.5) (2020-12-03)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-dicom-tag-browser
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## [0.0.4](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-tag-browser@0.0.3...@ohif/extension-dicom-tag-browser@0.0.4) (2020-10-07)
|
## [0.0.4](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-tag-browser@0.0.3...@ohif/extension-dicom-tag-browser@0.0.4) (2020-10-07)
|
||||||
|
|
||||||
**Note:** Version bump only for package @ohif/extension-dicom-tag-browser
|
**Note:** Version bump only for package @ohif/extension-dicom-tag-browser
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@ohif/extension-dicom-tag-browser",
|
"name": "@ohif/extension-dicom-tag-browser",
|
||||||
"version": "0.0.4",
|
"version": "0.2.15",
|
||||||
"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.16.5",
|
"dcmjs": "0.19.1",
|
||||||
"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.3",
|
||||||
"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 } 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';
|
||||||
@@ -10,6 +11,8 @@ const { ImageSet } = classes;
|
|||||||
const { DicomMetaDictionary } = dcmjs.data;
|
const { DicomMetaDictionary } = dcmjs.data;
|
||||||
const { nameMap } = DicomMetaDictionary;
|
const { nameMap } = DicomMetaDictionary;
|
||||||
|
|
||||||
|
const { metadataProvider } = OHIFCornerstone;
|
||||||
|
|
||||||
const DicomTagBrowser = ({ displaySets, displaySetInstanceUID }) => {
|
const DicomTagBrowser = ({ displaySets, displaySetInstanceUID }) => {
|
||||||
const [
|
const [
|
||||||
activeDisplaySetInstanceUID,
|
activeDisplaySetInstanceUID,
|
||||||
@@ -17,6 +20,7 @@ const DicomTagBrowser = ({ displaySets, displaySetInstanceUID }) => {
|
|||||||
] = useState(displaySetInstanceUID);
|
] = useState(displaySetInstanceUID);
|
||||||
const [activeInstance, setActiveInstance] = useState(0);
|
const [activeInstance, setActiveInstance] = useState(0);
|
||||||
const [tags, setTags] = useState([]);
|
const [tags, setTags] = useState([]);
|
||||||
|
const [meta, setMeta] = useState('');
|
||||||
const [instanceList, setInstanceList] = useState([]);
|
const [instanceList, setInstanceList] = useState([]);
|
||||||
const [displaySetList, setDisplaySetList] = useState([]);
|
const [displaySetList, setDisplaySetList] = useState([]);
|
||||||
const [isImageStack, setIsImageStack] = useState(false);
|
const [isImageStack, setIsImageStack] = useState(false);
|
||||||
@@ -53,9 +57,10 @@ const DicomTagBrowser = ({ displaySets, displaySetInstanceUID }) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
let metadata;
|
let metadata;
|
||||||
const isImageStack = activeDisplaySet instanceof ImageSet;
|
const isImageStack =
|
||||||
|
activeDisplaySet instanceof ImageSet &&
|
||||||
|
activeDisplaySet.isModalitySupported === true;
|
||||||
|
|
||||||
let selectedInstanceValue;
|
|
||||||
let instanceList;
|
let instanceList;
|
||||||
|
|
||||||
if (isImageStack) {
|
if (isImageStack) {
|
||||||
@@ -83,10 +88,11 @@ const DicomTagBrowser = ({ displaySets, displaySetInstanceUID }) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
setTags(getSortedTags(metadata));
|
setTags(getSortedTags(metadata));
|
||||||
|
setMeta(metadata);
|
||||||
setInstanceList(instanceList);
|
setInstanceList(instanceList);
|
||||||
setDisplaySetList(newDisplaySetList);
|
setDisplaySetList(newDisplaySetList);
|
||||||
setIsImageStack(isImageStack);
|
setIsImageStack(isImageStack);
|
||||||
}, [activeDisplaySetInstanceUID, activeInstance]);
|
}, [activeDisplaySetInstanceUID, activeInstance, displaySets]);
|
||||||
|
|
||||||
const selectedDisplaySetValue = displaySetList.find(
|
const selectedDisplaySetValue = displaySetList.find(
|
||||||
ds => ds.value === activeDisplaySetInstanceUID
|
ds => ds.value === activeDisplaySetInstanceUID
|
||||||
@@ -95,47 +101,56 @@ 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}></DicomTagTable>
|
<div className="dicom-tag-browser-table-wrapper">
|
||||||
|
<DicomTagTable tags={tags} meta={meta}></DicomTagTable>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
function DicomTagTable({ tags }) {
|
function DicomTagTable({ tags, meta }) {
|
||||||
const rows = getFormattedRowsFromTags(tags);
|
const rows = getFormattedRowsFromTags(tags, meta);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<table className="dicom-tag-browser-table">
|
||||||
<table className="dicom-tag-browser-table">
|
<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 => {
|
{rows.map((row, index) => {
|
||||||
const className = row.className ? row.className : null;
|
const className = row.className ? row.className : null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<tr className={className}>
|
<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>
|
||||||
@@ -143,12 +158,12 @@ function DicomTagTable({ tags }) {
|
|||||||
</tr>
|
</tr>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</table>
|
</tbody>
|
||||||
</div>
|
</table>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getFormattedRowsFromTags(tags) {
|
function getFormattedRowsFromTags(tags, meta) {
|
||||||
const rows = [];
|
const rows = [];
|
||||||
|
|
||||||
tags.forEach(tagInfo => {
|
tags.forEach(tagInfo => {
|
||||||
@@ -175,6 +190,21 @@ function getFormattedRowsFromTags(tags) {
|
|||||||
rows.push(...formatedRowsFromTags);
|
rows.push(...formatedRowsFromTags);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
if (tagInfo.vr === 'xs') {
|
||||||
|
try {
|
||||||
|
const dataset = metadataProvider.getStudyDataset(
|
||||||
|
meta.StudyInstanceUID
|
||||||
|
);
|
||||||
|
const tag = dcmjs.data.Tag.fromPString(tagInfo.tag).toCleanString();
|
||||||
|
const originalTagInfo = dataset[tag];
|
||||||
|
tagInfo.vr = originalTagInfo.vr;
|
||||||
|
} catch (error) {
|
||||||
|
console.error(
|
||||||
|
`Failed to parse value representation for tag '${tagInfo.keyword}'`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
rows.push([
|
rows.push([
|
||||||
`${tagInfo.tagIndent}${tagInfo.tag}`,
|
`${tagInfo.tagIndent}${tagInfo.tag}`,
|
||||||
tagInfo.vr,
|
tagInfo.vr,
|
||||||
@@ -247,10 +277,11 @@ function getRows(metadata, depth = 0) {
|
|||||||
sequenceAsArray.forEach(item => {
|
sequenceAsArray.forEach(item => {
|
||||||
const sequenceRows = getRows(item, depth + 1);
|
const sequenceRows = getRows(item, depth + 1);
|
||||||
|
|
||||||
// Sort the sequence group.
|
if (sequenceRows.length) {
|
||||||
_sortTagList(sequenceRows);
|
// Sort the sequence group.
|
||||||
|
_sortTagList(sequenceRows);
|
||||||
sequence.values.push(sequenceRows);
|
sequence.values.push(sequenceRows);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
@@ -276,13 +307,12 @@ function getRows(metadata, depth = 0) {
|
|||||||
} else if (value.Alphabetic) {
|
} else if (value.Alphabetic) {
|
||||||
value = value.Alphabetic;
|
value = value.Alphabetic;
|
||||||
} else {
|
} else {
|
||||||
console.error('Unrecognised Value for element:');
|
console.warn(`Unrecognised Value: ${value} for ${keyword}:`);
|
||||||
console.error(value);
|
console.warn(value);
|
||||||
value = ' ';
|
value = ' ';
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
console.error('Unrecognised Value for element:');
|
console.warn(`Unrecognised Value: ${value} for ${keyword}:`);
|
||||||
console.error(value);
|
|
||||||
value = ' ';
|
value = ' ';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ export default function getCommandsModule(servicesManager) {
|
|||||||
content: WrappedDicomTagBrowser,
|
content: WrappedDicomTagBrowser,
|
||||||
title: `DICOM Tag Browser`,
|
title: `DICOM Tag Browser`,
|
||||||
fullscreen: true,
|
fullscreen: true,
|
||||||
|
noScroll: true,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -3,6 +3,401 @@
|
|||||||
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.12.7](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.12.6...@ohif/extension-vtk@1.12.7) (2021-12-02)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-vtk
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [1.12.6](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.12.5...@ohif/extension-vtk@1.12.6) (2021-11-30)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-vtk
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [1.12.5](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.12.4...@ohif/extension-vtk@1.12.5) (2021-10-26)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-vtk
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [1.12.4](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.12.3...@ohif/extension-vtk@1.12.4) (2021-10-15)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-vtk
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [1.12.3](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.12.2...@ohif/extension-vtk@1.12.3) (2021-10-04)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-vtk
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [1.12.2](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.12.1...@ohif/extension-vtk@1.12.2) (2021-09-28)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-vtk
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [1.12.1](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.12.0...@ohif/extension-vtk@1.12.1) (2021-09-24)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-vtk
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# [1.12.0](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.11.18...@ohif/extension-vtk@1.12.0) (2021-09-23)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* Update to WADO Image Loader 4.0.3 to add dynamically imported WebAssembly codec support 🚀 ([#2537](https://github.com/OHIF/Viewers/issues/2537)) ([489d4b0](https://github.com/OHIF/Viewers/commit/489d4b0584f4657ceda1c5b9b68d5e9733827e47))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [1.11.18](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.11.17...@ohif/extension-vtk@1.11.18) (2021-09-13)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-vtk
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [1.11.17](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.11.16...@ohif/extension-vtk@1.11.17) (2021-09-09)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-vtk
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [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)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-vtk
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [1.10.1](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.10.0...@ohif/extension-vtk@1.10.1) (2020-12-10)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* panel issues and mpr button (IDC2122-IDC2117) ([32022f5](https://github.com/OHIF/Viewers/commit/32022f51f2d24f53f4c98188980db038a6dfe76c))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# [1.10.0](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.9.4...@ohif/extension-vtk@1.10.0) (2020-12-10)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* visualize overlapping segments in cornerstone ([#2185](https://github.com/OHIF/Viewers/issues/2185)) ([29fceac](https://github.com/OHIF/Viewers/commit/29fceacee97d51f1952a0f6b574c66596d32c201))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [1.9.4](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.9.3...@ohif/extension-vtk@1.9.4) (2020-12-03)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* reset VOI on mpr reset ([#2115](https://github.com/OHIF/Viewers/issues/2115)) ([0baf18d](https://github.com/OHIF/Viewers/commit/0baf18dcb2985378016236b735329346a851e14f))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [1.9.3](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.9.2...@ohif/extension-vtk@1.9.3) (2020-12-03)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-vtk
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [1.9.2](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.9.1...@ohif/extension-vtk@1.9.2) (2020-12-03)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-vtk
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [1.9.1](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.9.0...@ohif/extension-vtk@1.9.1) (2020-12-03)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-vtk
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# [1.9.0](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.8.0...@ohif/extension-vtk@1.9.0) (2020-12-03)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* Add error boundary and retry logic for network failures during dynamic imports ([#2145](https://github.com/OHIF/Viewers/issues/2145)) ([4c07904](https://github.com/OHIF/Viewers/commit/4c079044f6ae2381c6054d8d77414100152d1d19))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# [1.8.0](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.7.16...@ohif/extension-vtk@1.8.0) (2020-11-30)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **i18n:** Added de (German) translations ([2fff2d5](https://github.com/OHIF/Viewers/commit/2fff2d511fa1d36bdd6476da4ffa5684bf55374f))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [1.7.16](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.7.15...@ohif/extension-vtk@1.7.16) (2020-11-19)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-vtk
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [1.7.15](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.7.14...@ohif/extension-vtk@1.7.15) (2020-11-02)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-vtk
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [1.7.14](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.7.13...@ohif/extension-vtk@1.7.14) (2020-10-20)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-vtk
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [1.7.13](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.7.12...@ohif/extension-vtk@1.7.13) (2020-10-15)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-vtk
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [1.7.12](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.7.11...@ohif/extension-vtk@1.7.12) (2020-10-13)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-vtk
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [1.7.11](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.7.10...@ohif/extension-vtk@1.7.11) (2020-10-13)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-vtk
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## [1.7.10](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.7.9...@ohif/extension-vtk@1.7.10) (2020-10-07)
|
## [1.7.10](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.7.9...@ohif/extension-vtk@1.7.10) (2020-10-07)
|
||||||
|
|
||||||
**Note:** Version bump only for package @ohif/extension-vtk
|
**Note:** Version bump only for package @ohif/extension-vtk
|
||||||
|
|||||||
+12
-12
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@ohif/extension-vtk",
|
"name": "@ohif/extension-vtk",
|
||||||
"version": "1.7.10",
|
"version": "1.12.7",
|
||||||
"description": "OHIF extension for VTK.js",
|
"description": "OHIF extension for VTK.js",
|
||||||
"author": "OHIF",
|
"author": "OHIF",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
@@ -32,11 +32,11 @@
|
|||||||
"@ohif/core": "^0.50.0",
|
"@ohif/core": "^0.50.0",
|
||||||
"@ohif/i18n": "^0.50.0",
|
"@ohif/i18n": "^0.50.0",
|
||||||
"@ohif/ui": "^0.50.0",
|
"@ohif/ui": "^0.50.0",
|
||||||
"cornerstone-core": "^2.2.8",
|
"cornerstone-core": "^2.6.1",
|
||||||
"cornerstone-tools": "^4.20.1",
|
"cornerstone-tools": "^6.0.6",
|
||||||
"cornerstone-wado-image-loader": "^3.1.0",
|
"cornerstone-wado-image-loader": "^4.1.0",
|
||||||
"dcmjs": "0.16.5",
|
"dcmjs": "0.19.1",
|
||||||
"dicom-parser": "^1.8.3",
|
"dicom-parser": "^1.8.11",
|
||||||
"i18next": "^17.0.3",
|
"i18next": "^17.0.3",
|
||||||
"i18next-browser-languagedetector": "^3.0.1",
|
"i18next-browser-languagedetector": "^3.0.1",
|
||||||
"prop-types": "^15.7.2",
|
"prop-types": "^15.7.2",
|
||||||
@@ -50,14 +50,14 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "^7.5.5",
|
"@babel/runtime": "^7.5.5",
|
||||||
"lodash.throttle": "^4.1.1",
|
"lodash.throttle": "^4.1.1",
|
||||||
"react-vtkjs-viewport": "^0.10.4"
|
"react-vtkjs-viewport": "^0.14.4"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@ohif/core": "^2.10.7",
|
"@ohif/core": "^2.16.3",
|
||||||
"@ohif/ui": "^1.5.6",
|
"@ohif/ui": "^1.10.5",
|
||||||
"cornerstone-tools": "^4.20.1",
|
"cornerstone-tools": "^6.0.6",
|
||||||
"cornerstone-wado-image-loader": "^3.1.0",
|
"cornerstone-wado-image-loader": "^4.1.0",
|
||||||
"dicom-parser": "^1.8.3",
|
"dicom-parser": "^1.8.11",
|
||||||
"gh-pages": "^2.0.1",
|
"gh-pages": "^2.0.1",
|
||||||
"i18next": "^17.0.3",
|
"i18next": "^17.0.3",
|
||||||
"i18next-browser-languagedetector": "^3.0.1",
|
"i18next-browser-languagedetector": "^3.0.1",
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ class OHIFVTKViewport extends Component {
|
|||||||
};
|
};
|
||||||
|
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
onScroll: () => {},
|
onScroll: () => { },
|
||||||
};
|
};
|
||||||
|
|
||||||
static id = 'OHIFVTKViewport';
|
static id = 'OHIFVTKViewport';
|
||||||
@@ -138,6 +138,8 @@ class OHIFVTKViewport extends Component {
|
|||||||
SOPInstanceUID,
|
SOPInstanceUID,
|
||||||
frameIndex
|
frameIndex
|
||||||
) => {
|
) => {
|
||||||
|
const { UINotificationService } = this.props.servicesManager.services;
|
||||||
|
|
||||||
const stack = OHIFVTKViewport.getCornerstoneStack(
|
const stack = OHIFVTKViewport.getCornerstoneStack(
|
||||||
studies,
|
studies,
|
||||||
StudyInstanceUID,
|
StudyInstanceUID,
|
||||||
@@ -159,6 +161,18 @@ class OHIFVTKViewport extends Component {
|
|||||||
const { activeLabelmapIndex } = brushStackState;
|
const { activeLabelmapIndex } = brushStackState;
|
||||||
const labelmap3D = brushStackState.labelmaps3D[activeLabelmapIndex];
|
const labelmap3D = brushStackState.labelmaps3D[activeLabelmapIndex];
|
||||||
|
|
||||||
|
if (
|
||||||
|
brushStackState.labelmaps3D.length > 1 &&
|
||||||
|
this.props.viewportIndex === 0
|
||||||
|
) {
|
||||||
|
UINotificationService.show({
|
||||||
|
title: 'Overlapping Segmentation Found',
|
||||||
|
message:
|
||||||
|
'Overlapping segmentations cannot be displayed when in MPR mode',
|
||||||
|
type: 'info',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
this.segmentsDefaultProperties = labelmap3D.segmentsHidden.map(
|
this.segmentsDefaultProperties = labelmap3D.segmentsHidden.map(
|
||||||
isHidden => {
|
isHidden => {
|
||||||
return { visible: !isHidden };
|
return { visible: !isHidden };
|
||||||
@@ -257,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);
|
||||||
|
|
||||||
@@ -296,52 +310,82 @@ class OHIFVTKViewport extends Component {
|
|||||||
seriesDescription: displaySet.seriesDescription,
|
seriesDescription: displaySet.seriesDescription,
|
||||||
};
|
};
|
||||||
|
|
||||||
const {
|
try {
|
||||||
imageDataObject,
|
const {
|
||||||
labelmapDataObject,
|
imageDataObject,
|
||||||
labelmapColorLUT,
|
labelmapDataObject,
|
||||||
} = this.getViewportData(
|
labelmapColorLUT,
|
||||||
studies,
|
} = this.getViewportData(
|
||||||
StudyInstanceUID,
|
studies,
|
||||||
displaySetInstanceUID,
|
StudyInstanceUID,
|
||||||
SOPInstanceUID,
|
displaySetInstanceUID,
|
||||||
frameIndex
|
SOPInstanceUID,
|
||||||
);
|
frameIndex
|
||||||
|
);
|
||||||
|
|
||||||
this.imageDataObject = imageDataObject;
|
this.imageDataObject = imageDataObject;
|
||||||
|
|
||||||
/* TODO: Not currently used until we have drawing tools in vtkjs.
|
/* TODO: Not currently used until we have drawing tools in vtkjs.
|
||||||
if (!labelmap) {
|
if (!labelmap) {
|
||||||
labelmap = createLabelMapImageData(data);
|
labelmap = createLabelMapImageData(data);
|
||||||
} */
|
} */
|
||||||
|
|
||||||
const volumeActor = this.getOrCreateVolume(
|
const volumeActor = this.getOrCreateVolume(
|
||||||
imageDataObject,
|
imageDataObject,
|
||||||
displaySetInstanceUID
|
displaySetInstanceUID
|
||||||
);
|
);
|
||||||
|
|
||||||
this.setState(
|
this.setState(
|
||||||
{
|
{
|
||||||
percentComplete: 0,
|
percentComplete: 0,
|
||||||
dataDetails,
|
dataDetails,
|
||||||
},
|
},
|
||||||
() => {
|
() => {
|
||||||
this.loadProgressively(imageDataObject);
|
this.loadProgressively(imageDataObject);
|
||||||
|
|
||||||
// TODO: There must be a better way to do this.
|
// TODO: There must be a better way to do this.
|
||||||
// We do this so that if all the data is available the react-vtkjs-viewport
|
// We do this so that if all the data is available the react-vtkjs-viewport
|
||||||
// Will render _something_ before the volumes are set and the volume
|
// Will render _something_ before the volumes are set and the volume
|
||||||
// Construction that happens in react-vtkjs-viewport locks up the CPU.
|
// Construction that happens in react-vtkjs-viewport locks up the CPU.
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.setState({
|
this.setState({
|
||||||
volumes: [volumeActor],
|
volumes: [volumeActor],
|
||||||
paintFilterLabelMapImageData: labelmapDataObject,
|
paintFilterLabelMapImageData: labelmapDataObject,
|
||||||
paintFilterBackgroundImageData: imageDataObject.vtkImageData,
|
paintFilterBackgroundImageData: imageDataObject.vtkImageData,
|
||||||
labelmapColorLUT,
|
labelmapColorLUT,
|
||||||
});
|
});
|
||||||
}, 200);
|
}, 200);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
const errorTitle = 'Failed to load 2D MPR';
|
||||||
|
console.error(errorTitle, error);
|
||||||
|
const {
|
||||||
|
UINotificationService,
|
||||||
|
LoggerService,
|
||||||
|
} = this.props.servicesManager.services;
|
||||||
|
if (this.props.viewportIndex === 0) {
|
||||||
|
const message = error.message.includes('buffer')
|
||||||
|
? 'Dataset is too big to display in MPR'
|
||||||
|
: error.message;
|
||||||
|
LoggerService.error({ error, message });
|
||||||
|
UINotificationService.show({
|
||||||
|
title: errorTitle,
|
||||||
|
message,
|
||||||
|
type: 'error',
|
||||||
|
autoClose: false,
|
||||||
|
action: {
|
||||||
|
label: 'Exit 2D MPR',
|
||||||
|
onClick: ({ close }) => {
|
||||||
|
// context: 'ACTIVE_VIEWPORT::VTK',
|
||||||
|
close();
|
||||||
|
this.props.commandsManager.runCommand('setCornerstoneLayout');
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
}
|
}
|
||||||
);
|
this.setState({ isLoaded: true });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
@@ -354,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
|
||||||
) {
|
) {
|
||||||
@@ -387,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,
|
||||||
|
|||||||
@@ -1,37 +0,0 @@
|
|||||||
/**
|
|
||||||
* We use this component to leverage "Code Splitting"
|
|
||||||
*
|
|
||||||
* Link: https://serverless-stack.com/chapters/code-splitting-in-create-react-app.html
|
|
||||||
*/
|
|
||||||
|
|
||||||
import React, { Component } from 'react';
|
|
||||||
|
|
||||||
export default function asyncComponent(importComponent) {
|
|
||||||
class AsyncComponent extends Component {
|
|
||||||
constructor(props) {
|
|
||||||
super(props);
|
|
||||||
|
|
||||||
this.state = {
|
|
||||||
component: null,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
async componentDidMount() {
|
|
||||||
// Add dynamically loaded component to state
|
|
||||||
const { default: component } = await importComponent();
|
|
||||||
|
|
||||||
this.setState({
|
|
||||||
component: component,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const C = this.state.component;
|
|
||||||
|
|
||||||
// Render the loaded component, or null
|
|
||||||
return C ? <C {...this.props} /> : null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return AsyncComponent;
|
|
||||||
}
|
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
import throttle from 'lodash.throttle';
|
import throttle from 'lodash.throttle';
|
||||||
import {
|
import {
|
||||||
vtkInteractorStyleMPRCrosshairs,
|
|
||||||
vtkInteractorStyleMPRWindowLevel,
|
vtkInteractorStyleMPRWindowLevel,
|
||||||
|
vtkInteractorStyleRotatableMPRCrosshairs,
|
||||||
|
vtkSVGRotatableCrosshairsWidget,
|
||||||
vtkInteractorStyleMPRRotate,
|
vtkInteractorStyleMPRRotate,
|
||||||
vtkSVGCrosshairsWidget,
|
|
||||||
} from 'react-vtkjs-viewport';
|
} from 'react-vtkjs-viewport';
|
||||||
import { getImageData } from 'react-vtkjs-viewport';
|
import { getImageData } from 'react-vtkjs-viewport';
|
||||||
import { vec3 } from 'gl-matrix';
|
import { vec3 } from 'gl-matrix';
|
||||||
@@ -11,13 +11,15 @@ import setMPRLayout from './utils/setMPRLayout.js';
|
|||||||
import setViewportToVTK from './utils/setViewportToVTK.js';
|
import setViewportToVTK from './utils/setViewportToVTK.js';
|
||||||
import Constants from 'vtk.js/Sources/Rendering/Core/VolumeMapper/Constants.js';
|
import Constants from 'vtk.js/Sources/Rendering/Core/VolumeMapper/Constants.js';
|
||||||
import OHIFVTKViewport from './OHIFVTKViewport';
|
import OHIFVTKViewport from './OHIFVTKViewport';
|
||||||
import vtkCoordinate from 'vtk.js/Sources/Rendering/Core/Coordinate';
|
|
||||||
|
|
||||||
const { BlendMode } = Constants;
|
const { BlendMode } = Constants;
|
||||||
|
|
||||||
const commandsModule = ({ commandsManager }) => {
|
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;
|
||||||
|
|
||||||
async function _getActiveViewportVTKApi(viewports) {
|
async function _getActiveViewportVTKApi(viewports) {
|
||||||
const {
|
const {
|
||||||
@@ -119,6 +121,16 @@ const commandsModule = ({ commandsManager }) => {
|
|||||||
getVtkApis: ({ index }) => {
|
getVtkApis: ({ index }) => {
|
||||||
return apis[index];
|
return apis[index];
|
||||||
},
|
},
|
||||||
|
resetMPRView() {
|
||||||
|
// Reset orientation
|
||||||
|
apis.forEach(api => api.resetOrientation());
|
||||||
|
|
||||||
|
// Reset VOI
|
||||||
|
if (defaultVOI) setVOI(defaultVOI);
|
||||||
|
|
||||||
|
// Reset the crosshairs
|
||||||
|
apis[0].svgWidgets.rotatableCrosshairsWidget.resetCrosshairs(apis, 0);
|
||||||
|
},
|
||||||
axial: async ({ viewports }) => {
|
axial: async ({ viewports }) => {
|
||||||
const api = await _getActiveViewportVTKApi(viewports);
|
const api = await _getActiveViewportVTKApi(viewports);
|
||||||
|
|
||||||
@@ -165,7 +177,7 @@ const commandsModule = ({ commandsManager }) => {
|
|||||||
segmentNumber,
|
segmentNumber,
|
||||||
frameIndex,
|
frameIndex,
|
||||||
frame,
|
frame,
|
||||||
done = () => { }
|
done = () => {},
|
||||||
}) => {
|
}) => {
|
||||||
let api = apis[viewports.activeViewportIndex];
|
let api = apis[viewports.activeViewportIndex];
|
||||||
|
|
||||||
@@ -180,10 +192,13 @@ const commandsModule = ({ commandsManager }) => {
|
|||||||
displaySetInstanceUID,
|
displaySetInstanceUID,
|
||||||
SOPClassUID,
|
SOPClassUID,
|
||||||
SOPInstanceUID,
|
SOPInstanceUID,
|
||||||
frameIndex,
|
frameIndex
|
||||||
);
|
);
|
||||||
|
|
||||||
const imageDataObject = getImageData(stack.imageIds, displaySetInstanceUID);
|
const imageDataObject = getImageData(
|
||||||
|
stack.imageIds,
|
||||||
|
displaySetInstanceUID
|
||||||
|
);
|
||||||
|
|
||||||
let pixelIndex = 0;
|
let pixelIndex = 0;
|
||||||
let x = 0;
|
let x = 0;
|
||||||
@@ -209,9 +224,12 @@ const commandsModule = ({ commandsManager }) => {
|
|||||||
y /= count;
|
y /= count;
|
||||||
|
|
||||||
const position = [x, y, frameIndex];
|
const position = [x, y, frameIndex];
|
||||||
const worldPos = _convertModelToWorldSpace(position, imageDataObject.vtkImageData);
|
const worldPos = _convertModelToWorldSpace(
|
||||||
|
position,
|
||||||
|
imageDataObject.vtkImageData
|
||||||
|
);
|
||||||
|
|
||||||
api.svgWidgets.crosshairsWidget.moveCrosshairs(worldPos, apis);
|
api.svgWidgets.rotatableCrosshairsWidget.moveCrosshairs(worldPos, apis);
|
||||||
done();
|
done();
|
||||||
},
|
},
|
||||||
setSegmentationConfiguration: async ({
|
setSegmentationConfiguration: async ({
|
||||||
@@ -254,21 +272,38 @@ const commandsModule = ({ commandsManager }) => {
|
|||||||
await Promise.all(promises);
|
await Promise.all(promises);
|
||||||
},
|
},
|
||||||
enableRotateTool: () => {
|
enableRotateTool: () => {
|
||||||
apis.forEach(api => {
|
apis.forEach((api, apiIndex) => {
|
||||||
const istyle = vtkInteractorStyleMPRRotate.newInstance();
|
const istyle = vtkInteractorStyleMPRRotate.newInstance();
|
||||||
|
|
||||||
api.setInteractorStyle({ istyle });
|
api.setInteractorStyle({
|
||||||
|
istyle,
|
||||||
|
configuration: { apis, apiIndex, uid: api.uid },
|
||||||
|
});
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
enableCrosshairsTool: () => {
|
enableCrosshairsTool: () => {
|
||||||
apis.forEach((api, apiIndex) => {
|
apis.forEach((api, apiIndex) => {
|
||||||
const istyle = vtkInteractorStyleMPRCrosshairs.newInstance();
|
const istyle = vtkInteractorStyleRotatableMPRCrosshairs.newInstance();
|
||||||
|
|
||||||
api.setInteractorStyle({
|
api.setInteractorStyle({
|
||||||
istyle,
|
istyle,
|
||||||
configuration: { apis, apiIndex },
|
configuration: {
|
||||||
|
apis,
|
||||||
|
apiIndex,
|
||||||
|
uid: api.uid,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const rotatableCrosshairsWidget =
|
||||||
|
apis[0].svgWidgets.rotatableCrosshairsWidget;
|
||||||
|
|
||||||
|
const referenceLines = rotatableCrosshairsWidget.getReferenceLines();
|
||||||
|
|
||||||
|
// Initilise crosshairs if not initialised.
|
||||||
|
if (!referenceLines) {
|
||||||
|
rotatableCrosshairsWidget.resetCrosshairs(apis, 0);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
enableLevelTool: () => {
|
enableLevelTool: () => {
|
||||||
function updateVOI(apis, windowWidth, windowCenter) {
|
function updateVOI(apis, windowWidth, windowCenter) {
|
||||||
@@ -291,10 +326,14 @@ const commandsModule = ({ commandsManager }) => {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
apis.forEach(api => {
|
apis.forEach((api, apiIndex) => {
|
||||||
const istyle = vtkInteractorStyleMPRWindowLevel.newInstance();
|
const istyle = vtkInteractorStyleMPRWindowLevel.newInstance();
|
||||||
|
|
||||||
api.setInteractorStyle({ istyle, callbacks });
|
api.setInteractorStyle({
|
||||||
|
istyle,
|
||||||
|
callbacks,
|
||||||
|
configuration: { apis, apiIndex, uid: api.uid },
|
||||||
|
});
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
setSlabThickness: ({ slabThickness }) => {
|
setSlabThickness: ({ slabThickness }) => {
|
||||||
@@ -353,6 +392,7 @@ const commandsModule = ({ commandsManager }) => {
|
|||||||
|
|
||||||
// Get current VOI if cornerstone viewport.
|
// Get current VOI if cornerstone viewport.
|
||||||
const cornerstoneVOI = getVOIFromCornerstoneViewport();
|
const cornerstoneVOI = getVOIFromCornerstoneViewport();
|
||||||
|
defaultVOI = cornerstoneVOI;
|
||||||
|
|
||||||
const viewportProps = [
|
const viewportProps = [
|
||||||
{
|
{
|
||||||
@@ -391,18 +431,61 @@ const commandsModule = ({ commandsManager }) => {
|
|||||||
// Add widgets and set default interactorStyle of each viewport.
|
// Add widgets and set default interactorStyle of each viewport.
|
||||||
apis.forEach((api, apiIndex) => {
|
apis.forEach((api, apiIndex) => {
|
||||||
api.addSVGWidget(
|
api.addSVGWidget(
|
||||||
vtkSVGCrosshairsWidget.newInstance(),
|
vtkSVGRotatableCrosshairsWidget.newInstance(),
|
||||||
'crosshairsWidget'
|
'rotatableCrosshairsWidget'
|
||||||
);
|
);
|
||||||
|
|
||||||
const uid = api.uid;
|
const uid = api.uid;
|
||||||
const istyle = vtkInteractorStyleMPRCrosshairs.newInstance();
|
const istyle = vtkInteractorStyleRotatableMPRCrosshairs.newInstance();
|
||||||
|
|
||||||
api.setInteractorStyle({
|
api.setInteractorStyle({
|
||||||
istyle,
|
istyle,
|
||||||
configuration: { apis, apiIndex, uid },
|
configuration: { apis, apiIndex, uid },
|
||||||
});
|
});
|
||||||
|
|
||||||
|
api.svgWidgets.rotatableCrosshairsWidget.setApiIndex(apiIndex);
|
||||||
|
api.svgWidgets.rotatableCrosshairsWidget.setApis(apis);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const firstApi = apis[0];
|
||||||
|
|
||||||
|
// Initialise crosshairs
|
||||||
|
apis[0].svgWidgets.rotatableCrosshairsWidget.resetCrosshairs(apis, 0);
|
||||||
|
|
||||||
|
// Check if we have full WebGL 2 support
|
||||||
|
const openGLRenderWindow = apis[0].genericRenderWindow.getOpenGLRenderWindow();
|
||||||
|
|
||||||
|
if (!openGLRenderWindow.getWebgl2()) {
|
||||||
|
// Throw a warning if we don't have WebGL 2 support,
|
||||||
|
// And the volume is too big to fit in a 2D texture
|
||||||
|
|
||||||
|
const openGLContext = openGLRenderWindow.getContext();
|
||||||
|
const maxTextureSizeInBytes = openGLContext.getParameter(
|
||||||
|
openGLContext.MAX_TEXTURE_SIZE
|
||||||
|
);
|
||||||
|
|
||||||
|
const maxBufferLengthFloat32 =
|
||||||
|
(maxTextureSizeInBytes * maxTextureSizeInBytes) / 4;
|
||||||
|
|
||||||
|
const dimensions = firstApi.volumes[0]
|
||||||
|
.getMapper()
|
||||||
|
.getInputData()
|
||||||
|
.getDimensions();
|
||||||
|
|
||||||
|
const volumeLength = dimensions[0] * dimensions[1] * dimensions[2];
|
||||||
|
|
||||||
|
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({
|
||||||
|
title: 'Browser does not support WebGL 2',
|
||||||
|
message,
|
||||||
|
type: 'error',
|
||||||
|
autoClose: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -456,6 +539,10 @@ const commandsModule = ({ commandsManager }) => {
|
|||||||
commandFn: actions.enableLevelTool,
|
commandFn: actions.enableLevelTool,
|
||||||
options: {},
|
options: {},
|
||||||
},
|
},
|
||||||
|
resetMPRView: {
|
||||||
|
commandFn: actions.resetMPRView,
|
||||||
|
options: {},
|
||||||
|
},
|
||||||
setBlendModeToComposite: {
|
setBlendModeToComposite: {
|
||||||
commandFn: actions.setBlendModeToComposite,
|
commandFn: actions.setBlendModeToComposite,
|
||||||
options: { blendMode: BlendMode.COMPOSITE_BLEND },
|
options: { blendMode: BlendMode.COMPOSITE_BLEND },
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import asyncComponent from './asyncComponent.js';
|
import { asyncComponent, retryImport } from '@ohif/ui';
|
||||||
|
|
||||||
import commandsModule from './commandsModule.js';
|
import commandsModule from './commandsModule.js';
|
||||||
import toolbarModule from './toolbarModule.js';
|
import toolbarModule from './toolbarModule.js';
|
||||||
import withCommandsManager from './withCommandsManager.js';
|
import withCommandsManager from './withCommandsManager.js';
|
||||||
@@ -8,7 +9,9 @@ import { version } from '../package.json';
|
|||||||
// import loadLocales from './loadLocales';
|
// import loadLocales from './loadLocales';
|
||||||
|
|
||||||
const OHIFVTKViewport = asyncComponent(() =>
|
const OHIFVTKViewport = asyncComponent(() =>
|
||||||
import(/* webpackChunkName: "OHIFVTKViewport" */ './OHIFVTKViewport.js')
|
retryImport(() =>
|
||||||
|
import(/* webpackChunkName: "OHIFVTKViewport" */ './OHIFVTKViewport.js')
|
||||||
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
const vtkExtension = {
|
const vtkExtension = {
|
||||||
@@ -20,15 +23,19 @@ const vtkExtension = {
|
|||||||
|
|
||||||
getViewportModule({ commandsManager, servicesManager }) {
|
getViewportModule({ commandsManager, servicesManager }) {
|
||||||
const ExtendedVTKViewport = props => (
|
const ExtendedVTKViewport = props => (
|
||||||
<OHIFVTKViewport {...props} servicesManager={servicesManager} />
|
<OHIFVTKViewport
|
||||||
|
{...props}
|
||||||
|
servicesManager={servicesManager}
|
||||||
|
commandsManager={commandsManager}
|
||||||
|
/>
|
||||||
);
|
);
|
||||||
return withCommandsManager(ExtendedVTKViewport, commandsManager);
|
return withCommandsManager(ExtendedVTKViewport, commandsManager);
|
||||||
},
|
},
|
||||||
getToolbarModule() {
|
getToolbarModule() {
|
||||||
return toolbarModule;
|
return toolbarModule;
|
||||||
},
|
},
|
||||||
getCommandsModule({ commandsManager }) {
|
getCommandsModule({ commandsManager, servicesManager }) {
|
||||||
return commandsModule({ commandsManager });
|
return commandsModule({ commandsManager, servicesManager });
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"Rotate": "Drehen"
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
import Buttons from './Buttons.json';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
de: {
|
||||||
|
Buttons,
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -1,7 +1,9 @@
|
|||||||
import en from './en';
|
import en from './en';
|
||||||
import es from './es';
|
import es from './es';
|
||||||
|
import de from './de';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
...en,
|
...en,
|
||||||
...es
|
...es,
|
||||||
|
...de,
|
||||||
};
|
};
|
||||||
@@ -25,6 +25,16 @@ const definitions = [
|
|||||||
commandName: 'enableLevelTool',
|
commandName: 'enableLevelTool',
|
||||||
commandOptions: {},
|
commandOptions: {},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
id: 'Reset',
|
||||||
|
label: 'Reset',
|
||||||
|
icon: 'reset',
|
||||||
|
//
|
||||||
|
type: TOOLBAR_BUTTON_TYPES.COMMAND,
|
||||||
|
commandName: 'resetMPRView',
|
||||||
|
commandOptions: {},
|
||||||
|
},
|
||||||
|
/*
|
||||||
{
|
{
|
||||||
id: 'Rotate',
|
id: 'Rotate',
|
||||||
label: 'Rotate',
|
label: 'Rotate',
|
||||||
@@ -34,6 +44,7 @@ const definitions = [
|
|||||||
commandName: 'enableRotateTool',
|
commandName: 'enableRotateTool',
|
||||||
commandOptions: {},
|
commandOptions: {},
|
||||||
},
|
},
|
||||||
|
*/
|
||||||
/*
|
/*
|
||||||
{
|
{
|
||||||
id: 'setBlendModeToComposite',
|
id: 'setBlendModeToComposite',
|
||||||
@@ -131,4 +142,4 @@ const definitions = [
|
|||||||
export default {
|
export default {
|
||||||
definitions,
|
definitions,
|
||||||
defaultContext: 'ACTIVE_VIEWPORT::VTK',
|
defaultContext: 'ACTIVE_VIEWPORT::VTK',
|
||||||
}
|
};
|
||||||
@@ -28,6 +28,7 @@
|
|||||||
"test:unit": "jest --collectCoverage",
|
"test:unit": "jest --collectCoverage",
|
||||||
"test:unit:ci": "lerna run test:unit:ci --parallel --stream",
|
"test:unit:ci": "lerna run test:unit:ci --parallel --stream",
|
||||||
"test:e2e": "lerna run test:e2e --stream",
|
"test:e2e": "lerna run test:e2e --stream",
|
||||||
|
"test:e2e:script-tag": "lerna run test:e2e:script-tag --stream",
|
||||||
"test:e2e:ci": "lerna run test:e2e:ci --stream",
|
"test:e2e:ci": "lerna run test:e2e:ci --stream",
|
||||||
"test:e2e:dist": "lerna run test:e2e:dist --stream",
|
"test:e2e:dist": "lerna run test:e2e:dist --stream",
|
||||||
"test:e2e:serve": "lerna run test:e2e:serve --stream",
|
"test:e2e:serve": "lerna run test:e2e:serve --stream",
|
||||||
|
|||||||
@@ -3,6 +3,295 @@
|
|||||||
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.16.3](https://github.com/OHIF/Viewers/compare/@ohif/core@2.16.2...@ohif/core@2.16.3) (2021-12-02)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/core
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [2.16.2](https://github.com/OHIF/Viewers/compare/@ohif/core@2.16.1...@ohif/core@2.16.2) (2021-11-30)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/core
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [2.16.1](https://github.com/OHIF/Viewers/compare/@ohif/core@2.16.0...@ohif/core@2.16.1) (2021-10-26)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/core
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# [2.16.0](https://github.com/OHIF/Viewers/compare/@ohif/core@2.15.1...@ohif/core@2.16.0) (2021-10-04)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* Added support for WADO servers built with static hosting (e.g. S3) ([#2566](https://github.com/OHIF/Viewers/issues/2566)) ([a046c6c](https://github.com/OHIF/Viewers/commit/a046c6c9017a84e5ead71d5113b5e892333b4130))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [2.15.1](https://github.com/OHIF/Viewers/compare/@ohif/core@2.15.0...@ohif/core@2.15.1) (2021-09-28)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/core
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# [2.15.0](https://github.com/OHIF/Viewers/compare/@ohif/core@2.14.1...@ohif/core@2.15.0) (2021-09-23)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* Update to WADO Image Loader 4.0.3 to add dynamically imported WebAssembly codec support 🚀 ([#2537](https://github.com/OHIF/Viewers/issues/2537)) ([489d4b0](https://github.com/OHIF/Viewers/commit/489d4b0584f4657ceda1c5b9b68d5e9733827e47))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [2.14.1](https://github.com/OHIF/Viewers/compare/@ohif/core@2.14.0...@ohif/core@2.14.1) (2021-09-13)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/core
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# [2.14.0](https://github.com/OHIF/Viewers/compare/@ohif/core@2.13.15...@ohif/core@2.14.0) (2021-09-09)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* OHIF-1000: Study prefetcher ([#2466](https://github.com/OHIF/Viewers/issues/2466)) ([8f26045](https://github.com/OHIF/Viewers/commit/8f2604509d24092b721f1333289d838820d68cac))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [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)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/core
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# [2.12.0](https://github.com/OHIF/Viewers/compare/@ohif/core@2.11.1...@ohif/core@2.12.0) (2020-12-10)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* visualize overlapping segments in cornerstone ([#2185](https://github.com/OHIF/Viewers/issues/2185)) ([29fceac](https://github.com/OHIF/Viewers/commit/29fceacee97d51f1952a0f6b574c66596d32c201))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [2.11.1](https://github.com/OHIF/Viewers/compare/@ohif/core@2.11.0...@ohif/core@2.11.1) (2020-12-03)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* fallback to original dataset VR for XS ([f57725a](https://github.com/OHIF/Viewers/commit/f57725ac8bfd59ea46ab334a1823882afadefba1))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# [2.11.0](https://github.com/OHIF/Viewers/compare/@ohif/core@2.10.7...@ohif/core@2.11.0) (2020-12-03)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* add show scroll option to modal ([be494a7](https://github.com/OHIF/Viewers/commit/be494a7376d15777dbe598289c4ecdb9f48a6a48))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## [2.10.7](https://github.com/OHIF/Viewers/compare/@ohif/core@2.10.6...@ohif/core@2.10.7) (2020-10-07)
|
## [2.10.7](https://github.com/OHIF/Viewers/compare/@ohif/core@2.10.6...@ohif/core@2.10.7) (2020-10-07)
|
||||||
|
|
||||||
**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.10.7",
|
"version": "2.16.3",
|
||||||
"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",
|
||||||
@@ -31,21 +31,22 @@
|
|||||||
"test:unit:ci": "jest --ci --runInBand --collectCoverage"
|
"test:unit:ci": "jest --ci --runInBand --collectCoverage"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"cornerstone-core": "^2.2.8",
|
"cornerstone-core": "^2.6.1",
|
||||||
"cornerstone-tools": "^4.20.1",
|
"cornerstone-tools": "^6.0.6",
|
||||||
"cornerstone-wado-image-loader": "^3.1.0",
|
"cornerstone-wado-image-loader": "^4.1.0",
|
||||||
"dicom-parser": "^1.8.3"
|
"dicom-parser": "^1.8.11"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "^7.5.5",
|
"@babel/runtime": "^7.5.5",
|
||||||
"ajv": "^6.10.0",
|
"ajv": "^6.10.0",
|
||||||
"dcmjs": "0.16.5",
|
"dcmjs": "0.19.1",
|
||||||
"dicomweb-client": "^0.6.0",
|
"dicomweb-client": "^0.8.3",
|
||||||
"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);
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
import dcmjs from 'dcmjs';
|
import dcmjs from 'dcmjs';
|
||||||
|
import classes from '../classes';
|
||||||
|
|
||||||
import findInstanceMetadataBySopInstanceUID from './utils/findInstanceMetadataBySopInstanceUid';
|
import findInstanceMetadataBySopInstanceUID from './utils/findInstanceMetadataBySopInstanceUid';
|
||||||
|
|
||||||
|
const { LogManager } = classes;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function to parse the part10 array buffer that comes from a DICOM Structured report into measurementData
|
* Function to parse the part10 array buffer that comes from a DICOM Structured report into measurementData
|
||||||
* measurementData format is a viewer specific format to be stored into the redux and consumed by other components
|
* measurementData format is a viewer specific format to be stored into the redux and consumed by other components
|
||||||
@@ -20,9 +23,21 @@ const parseDicomStructuredReport = (part10SRArrayBuffer, displaySets) => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const { MeasurementReport } = dcmjs.adapters.Cornerstone;
|
const { MeasurementReport } = dcmjs.adapters.Cornerstone;
|
||||||
const storedMeasurementByToolType = MeasurementReport.generateToolState(
|
|
||||||
dataset
|
let storedMeasurementByToolType;
|
||||||
);
|
try {
|
||||||
|
storedMeasurementByToolType = MeasurementReport.generateToolState(dataset);
|
||||||
|
} catch (error) {
|
||||||
|
const seriesDescription = dataset.SeriesDescription || '';
|
||||||
|
LogManager.publish(LogManager.EVENTS.OnLog, {
|
||||||
|
title: `Failed to parse ${seriesDescription} measurement report`,
|
||||||
|
type: 'warning',
|
||||||
|
message: error.message || '',
|
||||||
|
notify: true,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const measurementData = {};
|
const measurementData = {};
|
||||||
let measurementNumber = 0;
|
let measurementNumber = 0;
|
||||||
|
|
||||||
@@ -77,7 +92,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',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
import PubSub from './PubSub';
|
||||||
|
|
||||||
|
/** Log Events */
|
||||||
|
export const LogEvents = Object.freeze({
|
||||||
|
OnLog: 'onLog',
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Log manager that implements pub/sub.
|
||||||
|
* This manager can be used to send logs across different packages
|
||||||
|
* using previously registered events.
|
||||||
|
*/
|
||||||
|
class LogManager extends PubSub {
|
||||||
|
EVENTS = LogEvents;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Singleton */
|
||||||
|
export default new LogManager();
|
||||||
@@ -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() {
|
||||||
@@ -20,6 +21,7 @@ class MetadataProvider {
|
|||||||
writable: false,
|
writable: false,
|
||||||
value: new Map(),
|
value: new Map(),
|
||||||
});
|
});
|
||||||
|
this.datasets = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
async addInstance(dicomJSONDatasetOrP10ArrayBuffer, options = {}) {
|
async addInstance(dicomJSONDatasetOrP10ArrayBuffer, options = {}) {
|
||||||
@@ -52,6 +54,7 @@ class MetadataProvider {
|
|||||||
SOPInstanceUID,
|
SOPInstanceUID,
|
||||||
} = naturalizedDataset;
|
} = naturalizedDataset;
|
||||||
|
|
||||||
|
this._getAndCacheStudyDataset(StudyInstanceUID, dicomJSONDataset);
|
||||||
const study = this._getAndCacheStudy(StudyInstanceUID);
|
const study = this._getAndCacheStudy(StudyInstanceUID);
|
||||||
const series = this._getAndCacheSeriesFromStudy(study, SeriesInstanceUID);
|
const series = this._getAndCacheSeriesFromStudy(study, SeriesInstanceUID);
|
||||||
const instance = this._getAndCacheInstanceFromStudy(series, SOPInstanceUID);
|
const instance = this._getAndCacheInstanceFromStudy(series, SOPInstanceUID);
|
||||||
@@ -71,6 +74,16 @@ class MetadataProvider {
|
|||||||
this.imageIdToUIDs.set(imageId, uids);
|
this.imageIdToUIDs.set(imageId, uids);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_getAndCacheStudyDataset(StudyInstanceUID, dataset) {
|
||||||
|
if (!this.datasets[StudyInstanceUID]) {
|
||||||
|
this.datasets[StudyInstanceUID] = dataset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getStudyDataset(StudyInstanceUID) {
|
||||||
|
return this.datasets[StudyInstanceUID];
|
||||||
|
}
|
||||||
|
|
||||||
_getAndCacheStudy(StudyInstanceUID) {
|
_getAndCacheStudy(StudyInstanceUID) {
|
||||||
const studies = this.studies;
|
const studies = this.studies;
|
||||||
|
|
||||||
@@ -83,6 +96,7 @@ class MetadataProvider {
|
|||||||
|
|
||||||
return study;
|
return study;
|
||||||
}
|
}
|
||||||
|
|
||||||
_getAndCacheSeriesFromStudy(study, SeriesInstanceUID) {
|
_getAndCacheSeriesFromStudy(study, SeriesInstanceUID) {
|
||||||
let series = study.series.get(SeriesInstanceUID);
|
let series = study.series.get(SeriesInstanceUID);
|
||||||
|
|
||||||
@@ -217,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,
|
||||||
};
|
};
|
||||||
@@ -271,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
|
||||||
@@ -281,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;
|
||||||
|
|||||||
@@ -0,0 +1,75 @@
|
|||||||
|
const _subscriptions = Symbol('subscriptions');
|
||||||
|
const _lastSubscriptionId = Symbol('lastSubscriptionId');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class to implement publish/subscribe pattern
|
||||||
|
*
|
||||||
|
* @class
|
||||||
|
* @classdesc Pub/sub mechanism
|
||||||
|
*/
|
||||||
|
export default class PubSub {
|
||||||
|
constructor() {
|
||||||
|
this[_subscriptions] = {};
|
||||||
|
this[_lastSubscriptionId] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Subscribe to event
|
||||||
|
*
|
||||||
|
* @param {string} eventName Event name
|
||||||
|
* @param {Function} callback Callback function
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
subscribe(eventName, callback) {
|
||||||
|
if (eventName === undefined) {
|
||||||
|
throw new Error('Event name is required');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof callback !== 'function') {
|
||||||
|
throw new Error('Callback must be a function');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this[_subscriptions].hasOwnProperty(eventName)) {
|
||||||
|
this[_subscriptions][eventName] = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
const subscriptionId = `sub${this[_lastSubscriptionId]++}`;
|
||||||
|
this[_subscriptions][eventName][subscriptionId] = callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes a subscription
|
||||||
|
*
|
||||||
|
* @param {string} eventName Event name
|
||||||
|
* @param {Function} [callback] Callback function
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
unsubscribe(eventName, callback) {
|
||||||
|
const callbacks = this[_subscriptions][eventName] || {};
|
||||||
|
for (let subscriptionId in callbacks) {
|
||||||
|
if (!callback) {
|
||||||
|
delete callbacks[subscriptionId];
|
||||||
|
} else if (callbacks[subscriptionId] === callback) {
|
||||||
|
delete callbacks[subscriptionId];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Publish event to all subscriptions
|
||||||
|
*
|
||||||
|
* @param {String} eventName Event name
|
||||||
|
* @param {any} [payload] Data that will be published
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
publish(eventName, ...payload) {
|
||||||
|
if (eventName === undefined) {
|
||||||
|
throw new Error('Event name is required');
|
||||||
|
}
|
||||||
|
|
||||||
|
const callbacks = this[_subscriptions][eventName] || {};
|
||||||
|
for (let subscriptionId in callbacks) {
|
||||||
|
callbacks[subscriptionId](...payload);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,16 +1,14 @@
|
|||||||
import cornerstone from 'cornerstone-core';
|
import cornerstone from 'cornerstone-core';
|
||||||
import cornerstoneWADOImageLoader from 'cornerstone-wado-image-loader';
|
import cornerstoneWADOImageLoader from 'cornerstone-wado-image-loader';
|
||||||
import {
|
import debounce from 'lodash.debounce';
|
||||||
clearStudyLoadingProgress,
|
|
||||||
setStudyLoadingProgress,
|
|
||||||
} from '../redux/actions';
|
|
||||||
import StackManager from '../utils/StackManager';
|
import StackManager from '../utils/StackManager';
|
||||||
|
import { StudyPrefetcher } from './StudyPrefetcher';
|
||||||
|
|
||||||
class BaseLoadingListener {
|
class BaseLoadingListener {
|
||||||
constructor(stack, options = {}) {
|
constructor(stack, options = {}) {
|
||||||
this.id = BaseLoadingListener.getNewId();
|
this.id = BaseLoadingListener.getNewId();
|
||||||
this.stack = stack;
|
this.stack = stack;
|
||||||
this.startListening();
|
|
||||||
this.statsItemsLimit = options.statsItemsLimit || 2;
|
this.statsItemsLimit = options.statsItemsLimit || 2;
|
||||||
this.stats = {
|
this.stats = {
|
||||||
items: [],
|
items: [],
|
||||||
@@ -60,6 +58,9 @@ class BaseLoadingListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_getProgressId() {
|
_getProgressId() {
|
||||||
|
/**
|
||||||
|
* TODO: The id key should be configurable.
|
||||||
|
*/
|
||||||
const displaySetInstanceUID = this.stack.displaySetInstanceUID;
|
const displaySetInstanceUID = this.stack.displaySetInstanceUID;
|
||||||
return 'StackProgress:' + displaySetInstanceUID;
|
return 'StackProgress:' + displaySetInstanceUID;
|
||||||
}
|
}
|
||||||
@@ -96,11 +97,18 @@ class BaseLoadingListener {
|
|||||||
class DICOMFileLoadingListener extends BaseLoadingListener {
|
class DICOMFileLoadingListener extends BaseLoadingListener {
|
||||||
constructor(stack, options) {
|
constructor(stack, options) {
|
||||||
super(stack, options);
|
super(stack, options);
|
||||||
|
|
||||||
|
this.imageLoadProgressEventHandler = this._imageLoadProgressEventHandle.bind(
|
||||||
|
this
|
||||||
|
);
|
||||||
|
|
||||||
this._dataSetUrl = this._getDataSetUrl(stack);
|
this._dataSetUrl = this._getDataSetUrl(stack);
|
||||||
this._lastLoaded = 0;
|
this._lastLoaded = 0;
|
||||||
|
|
||||||
// Check how many instances has already been download (cached)
|
// Check how many instances has already been download (cached)
|
||||||
this._checkCachedData();
|
this._checkCachedData();
|
||||||
|
|
||||||
|
this.startListening();
|
||||||
}
|
}
|
||||||
|
|
||||||
_checkCachedData() {
|
_checkCachedData() {
|
||||||
@@ -127,10 +135,6 @@ class DICOMFileLoadingListener extends BaseLoadingListener {
|
|||||||
startListening() {
|
startListening() {
|
||||||
const imageLoadProgressEventName = this._getImageLoadProgressEventName();
|
const imageLoadProgressEventName = this._getImageLoadProgressEventName();
|
||||||
|
|
||||||
this.imageLoadProgressEventHandler = this._imageLoadProgressEventHandle.bind(
|
|
||||||
this
|
|
||||||
);
|
|
||||||
|
|
||||||
this.stopListening();
|
this.stopListening();
|
||||||
|
|
||||||
cornerstone.events.addEventListener(
|
cornerstone.events.addEventListener(
|
||||||
@@ -200,17 +204,57 @@ class DICOMFileLoadingListener extends BaseLoadingListener {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const StudyLoadingListenerEvents = {
|
||||||
|
OnProgress: 'StudyLoadingListenerEvents.OnProgress',
|
||||||
|
};
|
||||||
|
|
||||||
|
function promiseState(promise, callback) {
|
||||||
|
// Symbols and RegExps are never content-equal
|
||||||
|
var uniqueValue = window['Symbol'] ? Symbol('unique') : /unique/;
|
||||||
|
|
||||||
|
function notifyPendingOrResolved(value) {
|
||||||
|
if (value === uniqueValue) {
|
||||||
|
return callback('pending');
|
||||||
|
} else {
|
||||||
|
return callback('fulfilled');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function notifyRejected(reason) {
|
||||||
|
return callback('rejected');
|
||||||
|
}
|
||||||
|
|
||||||
|
var race = [promise, Promise.resolve(uniqueValue)];
|
||||||
|
Promise.race(race).then(notifyPendingOrResolved, notifyRejected);
|
||||||
|
}
|
||||||
|
|
||||||
class StackLoadingListener extends BaseLoadingListener {
|
class StackLoadingListener extends BaseLoadingListener {
|
||||||
constructor(stack, options = {}) {
|
constructor(stack, options = {}) {
|
||||||
options.statsItemsLimit = 20;
|
options.statsItemsLimit = 20;
|
||||||
|
|
||||||
super(stack, options);
|
super(stack, options);
|
||||||
|
|
||||||
|
this.imageLoadedEventHandler = this._imageLoadedEventHandler.bind(this);
|
||||||
|
this.imageCachePromiseRemovedEventHandler = this._imageCachePromiseRemovedEventHandler.bind(
|
||||||
|
this
|
||||||
|
);
|
||||||
|
|
||||||
this.imageDataMap = this._convertImageIdsArrayToMap(stack.imageIds);
|
this.imageDataMap = this._convertImageIdsArrayToMap(stack.imageIds);
|
||||||
this.framesStatus = this._createArray(stack.imageIds.length, false);
|
this.framesStatus = this._createArray(stack.imageIds.length, false);
|
||||||
this.loadedCount = 0;
|
this.loadedCount = 0;
|
||||||
|
|
||||||
// Check how many instances has already been download (cached)
|
// Check how many instances has already been download (cached)
|
||||||
this._checkCachedData();
|
this._debouncedSetProgressData = debounce((...args) => {
|
||||||
|
this._setProgressData(...args);
|
||||||
|
|
||||||
|
/** After checking cache, continue prefetch */
|
||||||
|
const studyPrefetcher = StudyPrefetcher.getInstance();
|
||||||
|
studyPrefetcher.prefetch(studyPrefetcher.getElement());
|
||||||
|
}, 300);
|
||||||
|
const debounced = true;
|
||||||
|
this._checkCachedData(debounced);
|
||||||
|
|
||||||
|
this.startListening();
|
||||||
}
|
}
|
||||||
|
|
||||||
_convertImageIdsArrayToMap(imageIds) {
|
_convertImageIdsArrayToMap(imageIds) {
|
||||||
@@ -238,18 +282,37 @@ class StackLoadingListener extends BaseLoadingListener {
|
|||||||
return array;
|
return array;
|
||||||
}
|
}
|
||||||
|
|
||||||
_checkCachedData() {
|
/**
|
||||||
// const imageIds = this.stack.imageIds;
|
* Check if image id is cached in cornerstone.
|
||||||
// TODO: No way to check status of Promise.
|
*
|
||||||
/*for(let i = 0; i < imageIds.length; i++) {
|
* @param {string} imageId
|
||||||
const imageId = imageIds[i];
|
* @returns
|
||||||
|
*/
|
||||||
|
isImageCached(imageId) {
|
||||||
|
const image = cornerstone.imageCache.imageCache[imageId];
|
||||||
|
return image && image.sizeInBytes;
|
||||||
|
}
|
||||||
|
|
||||||
const imagePromise = cornerstone.imageCache.getImageLoadObject(imageId).promise;
|
_checkCachedData(debounced = false) {
|
||||||
|
const imageIds = this.stack.imageIds;
|
||||||
|
|
||||||
if (imagePromise && (imagePromise.state() === 'resolved')) {
|
for (let i = 0; i < imageIds.length; i++) {
|
||||||
this._updateFrameStatus(imageId, true);
|
const imageId = imageIds[i];
|
||||||
}
|
|
||||||
}*/
|
const imageObject = cornerstone.imageCache.getImageLoadObject(imageId);
|
||||||
|
|
||||||
|
if (this.isImageCached(imageId)) {
|
||||||
|
this._updateFrameStatus(imageId, true, debounced);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (imageObject && imageObject.promise) {
|
||||||
|
promiseState(imageObject.promise, state => {
|
||||||
|
if (state === 'fulfilled') {
|
||||||
|
this._updateFrameStatus(imageId, true, debounced);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_getImageLoadedEventName() {
|
_getImageLoadedEventName() {
|
||||||
@@ -272,11 +335,6 @@ class StackLoadingListener extends BaseLoadingListener {
|
|||||||
const imageLoadedEventName = this._getImageLoadedEventName();
|
const imageLoadedEventName = this._getImageLoadedEventName();
|
||||||
const imageCachePromiseRemovedEventName = this._getImageCachePromiseRemoveEventName();
|
const imageCachePromiseRemovedEventName = this._getImageCachePromiseRemoveEventName();
|
||||||
|
|
||||||
this.imageLoadedEventHandler = this._imageLoadedEventHandler.bind(this);
|
|
||||||
this.imageCachePromiseRemovedEventHandler = this._imageCachePromiseRemovedEventHandler.bind(
|
|
||||||
this
|
|
||||||
);
|
|
||||||
|
|
||||||
this.stopListening();
|
this.stopListening();
|
||||||
|
|
||||||
cornerstone.events.addEventListener(
|
cornerstone.events.addEventListener(
|
||||||
@@ -303,7 +361,7 @@ class StackLoadingListener extends BaseLoadingListener {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
_updateFrameStatus(imageId, loaded) {
|
_updateFrameStatus(imageId, loaded, debounced) {
|
||||||
const imageData = this.imageDataMap.get(imageId);
|
const imageData = this.imageDataMap.get(imageId);
|
||||||
|
|
||||||
if (!imageData || imageData.loaded === loaded) {
|
if (!imageData || imageData.loaded === loaded) {
|
||||||
@@ -318,7 +376,7 @@ class StackLoadingListener extends BaseLoadingListener {
|
|||||||
imageData.loaded = loaded;
|
imageData.loaded = loaded;
|
||||||
this.framesStatus[imageData.index] = loaded;
|
this.framesStatus[imageData.index] = loaded;
|
||||||
this.loadedCount += loaded ? 1 : -1;
|
this.loadedCount += loaded ? 1 : -1;
|
||||||
this._updateProgress();
|
this._updateProgress(debounced);
|
||||||
}
|
}
|
||||||
|
|
||||||
_setProgressData(progressId, progressData) {
|
_setProgressData(progressId, progressData) {
|
||||||
@@ -339,7 +397,7 @@ class StackLoadingListener extends BaseLoadingListener {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
_updateProgress() {
|
_updateProgress(debounced) {
|
||||||
const totalFramesCount = this.stack.imageIds.length;
|
const totalFramesCount = this.stack.imageIds.length;
|
||||||
const loadedFramesCount = this.loadedCount;
|
const loadedFramesCount = this.loadedCount;
|
||||||
const loadingFramesCount = totalFramesCount - loadedFramesCount;
|
const loadingFramesCount = totalFramesCount - loadedFramesCount;
|
||||||
@@ -357,6 +415,11 @@ class StackLoadingListener extends BaseLoadingListener {
|
|||||||
framesStatus: this.framesStatus,
|
framesStatus: this.framesStatus,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (debounced) {
|
||||||
|
this._debouncedSetProgressData(progressId, progressData);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
this._setProgressData(progressId, progressData);
|
this._setProgressData(progressId, progressData);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -371,11 +434,13 @@ class StackLoadingListener extends BaseLoadingListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
progressBar += ']';
|
progressBar += ']';
|
||||||
log.info(`${displaySetInstanceUID}: ${progressBar}`);
|
console.info(`${displaySetInstanceUID}: ${progressBar}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class StudyLoadingListener {
|
class StudyLoadingListener {
|
||||||
|
static events = StudyLoadingListenerEvents;
|
||||||
|
|
||||||
constructor(options) {
|
constructor(options) {
|
||||||
this.listeners = {};
|
this.listeners = {};
|
||||||
this.options = options;
|
this.options = options;
|
||||||
@@ -454,14 +519,36 @@ class StudyLoadingListener {
|
|||||||
|
|
||||||
_getSchema(stack) {
|
_getSchema(stack) {
|
||||||
const imageId = stack.imageIds[0];
|
const imageId = stack.imageIds[0];
|
||||||
|
if (!imageId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
const colonIndex = imageId.indexOf(':');
|
const colonIndex = imageId.indexOf(':');
|
||||||
return imageId.substring(0, colonIndex);
|
return imageId.substring(0, colonIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Singleton
|
|
||||||
static getInstance(options) {
|
static getInstance(options) {
|
||||||
|
/**
|
||||||
|
* TODO: Use a different alternative without the use of events.
|
||||||
|
*/
|
||||||
|
const DEFAULT_OPTIONS = {
|
||||||
|
_setProgressData: (progressId, progressData) => {
|
||||||
|
const event = new CustomEvent(StudyLoadingListenerEvents.OnProgress, {
|
||||||
|
detail: { progressId, progressData },
|
||||||
|
});
|
||||||
|
document.dispatchEvent(event);
|
||||||
|
},
|
||||||
|
_clearProgressById: progressId => {
|
||||||
|
const event = new CustomEvent(StudyLoadingListenerEvents.OnProgress, {
|
||||||
|
detail: { progressId, percentComplete: 0 },
|
||||||
|
});
|
||||||
|
document.dispatchEvent(event);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
if (!StudyLoadingListener._instance) {
|
if (!StudyLoadingListener._instance) {
|
||||||
StudyLoadingListener._instance = new StudyLoadingListener(options);
|
StudyLoadingListener._instance = new StudyLoadingListener(
|
||||||
|
options || DEFAULT_OPTIONS
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return StudyLoadingListener._instance;
|
return StudyLoadingListener._instance;
|
||||||
|
|||||||
@@ -1,14 +1,26 @@
|
|||||||
import log from '../log.js';
|
|
||||||
import OHIFError from './OHIFError';
|
|
||||||
import cornerstone from 'cornerstone-core';
|
import cornerstone from 'cornerstone-core';
|
||||||
import cornerstoneTools from 'cornerstone-tools';
|
|
||||||
import getImageId from '../utils/getImageId.js';
|
import getImageId from '../utils/getImageId.js';
|
||||||
|
|
||||||
|
const noop = () => {};
|
||||||
|
|
||||||
export class StudyPrefetcher {
|
export class StudyPrefetcher {
|
||||||
constructor(studies) {
|
options = {
|
||||||
|
order: 'closest',
|
||||||
|
displaySetCount: 1,
|
||||||
|
onImageCached: noop,
|
||||||
|
requestType: 'prefetch',
|
||||||
|
preventCache: false,
|
||||||
|
prefetchDisplaySetsTimeout: 300,
|
||||||
|
includeActiveDisplaySet: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
constructor(studies, options) {
|
||||||
this.studies = studies || [];
|
this.studies = studies || [];
|
||||||
this.prefetchDisplaySetsTimeout = 300;
|
|
||||||
this.lastActiveViewportElement = null;
|
if (options) {
|
||||||
|
this.options = { ...this.options, ...options };
|
||||||
|
this.options.requestType = 'prefetch';
|
||||||
|
}
|
||||||
|
|
||||||
cornerstone.events.addEventListener(
|
cornerstone.events.addEventListener(
|
||||||
'cornerstoneimagecachefull.StudyPrefetcher',
|
'cornerstoneimagecachefull.StudyPrefetcher',
|
||||||
@@ -16,6 +28,9 @@ export class StudyPrefetcher {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove previously set event listeners and stop prefetching.
|
||||||
|
*/
|
||||||
destroy() {
|
destroy() {
|
||||||
this.stopPrefetching();
|
this.stopPrefetching();
|
||||||
cornerstone.events.removeEventListener(
|
cornerstone.events.removeEventListener(
|
||||||
@@ -24,95 +39,175 @@ export class StudyPrefetcher {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
static getInstance() {
|
/**
|
||||||
|
* Get StudyPrefetcher singleton instance.
|
||||||
|
*
|
||||||
|
* @param {array} studies
|
||||||
|
* @param {object} options
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
static getInstance(studies = [], options) {
|
||||||
if (!StudyPrefetcher.instance) {
|
if (!StudyPrefetcher.instance) {
|
||||||
StudyPrefetcher.instance = new StudyPrefetcher([]);
|
StudyPrefetcher.instance = new StudyPrefetcher(studies, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options) {
|
||||||
|
this.options = { ...this.options, ...options };
|
||||||
|
this.options.requestType = 'prefetch';
|
||||||
}
|
}
|
||||||
|
|
||||||
return StudyPrefetcher.instance;
|
return StudyPrefetcher.instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* OHIF study metadata instances.
|
||||||
|
*
|
||||||
|
* @param {array} studies
|
||||||
|
*/
|
||||||
setStudies(studies) {
|
setStudies(studies) {
|
||||||
this.stopPrefetching();
|
this.stopPrefetching();
|
||||||
this.studies = studies;
|
this.studies = studies;
|
||||||
}
|
}
|
||||||
|
|
||||||
prefetch() {
|
/**
|
||||||
|
* Get previously prefetched element.
|
||||||
|
*
|
||||||
|
* @returns {HTMLElement} Previously prefetched element.
|
||||||
|
*/
|
||||||
|
getElement() {
|
||||||
|
return this.element;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prefetch related display sets based on cornerstone viewport element
|
||||||
|
* with previously set options.
|
||||||
|
*
|
||||||
|
* @param {*} element
|
||||||
|
* @param {string} displaySetInstanceUID the display set instance uid
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
prefetch(element, displaySetInstanceUID) {
|
||||||
if (!this.studies || !this.studies.length) {
|
if (!this.studies || !this.studies.length) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.element = element;
|
||||||
|
try {
|
||||||
|
this.enabledElement = cornerstone.getEnabledElement(element);
|
||||||
|
} catch {
|
||||||
|
throw new Error('Failed to find the enabled element');
|
||||||
|
}
|
||||||
|
|
||||||
this.stopPrefetching();
|
this.stopPrefetching();
|
||||||
this.prefetchDisplaySets();
|
this.prefetchDisplaySets(displaySetInstanceUID);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stop prefetching images.
|
||||||
|
*/
|
||||||
stopPrefetching() {
|
stopPrefetching() {
|
||||||
cornerstoneTools.requestPoolManager.clearRequestStack('prefetch');
|
cornerstone.imageLoadPoolManager.clearRequestStack('prefetch');
|
||||||
}
|
}
|
||||||
|
|
||||||
prefetchDisplaySetsAsync(timeout) {
|
/**
|
||||||
timeout = timeout || this.prefetchDisplaySetsTimeout;
|
* Prefetch display sets async.
|
||||||
|
*
|
||||||
|
* @param {HTMLElement} element cornerstone viewport element
|
||||||
|
* @param {number} timeout
|
||||||
|
*/
|
||||||
|
prefetchDisplaySetsAsync(element, timeout) {
|
||||||
|
try {
|
||||||
|
this.enabledElement = cornerstone.getEnabledElement(element);
|
||||||
|
} catch {
|
||||||
|
throw new Error('Failed to find the enabled element');
|
||||||
|
}
|
||||||
|
timeout = timeout || this.options.prefetchDisplaySetsTimeout;
|
||||||
clearTimeout(this.prefetchDisplaySetsHandler);
|
clearTimeout(this.prefetchDisplaySetsHandler);
|
||||||
this.prefetchDisplaySetsHandler = setTimeout(() => {
|
this.prefetchDisplaySetsHandler = setTimeout(() => {
|
||||||
this.prefetchDisplaySets();
|
this.prefetchDisplaySets(element);
|
||||||
}, timeout);
|
}, timeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
prefetchDisplaySets() {
|
/**
|
||||||
// TODO: Allow passing in config
|
* Extract all image ids from all display sets to be fetched and
|
||||||
let config = {
|
* call method to add images to request pool manager.
|
||||||
order: 'closest',
|
*
|
||||||
displaySetCount: 1,
|
* @param {string} displaySetInstanceUID the display set instance uid
|
||||||
};
|
*/
|
||||||
|
prefetchDisplaySets(displaySetInstanceUID) {
|
||||||
const displaySetsToPrefetch = this.getDisplaySetsToPrefetch(config);
|
const displaySetsToPrefetch = this.getDisplaySetsToPrefetch(
|
||||||
|
displaySetInstanceUID
|
||||||
|
);
|
||||||
const imageIds = this.getImageIdsFromDisplaySets(displaySetsToPrefetch);
|
const imageIds = this.getImageIdsFromDisplaySets(displaySetsToPrefetch);
|
||||||
|
|
||||||
this.prefetchImageIds(imageIds);
|
this.prefetchImageIds(imageIds);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add image ids to request pool manager.
|
||||||
|
*
|
||||||
|
* @param {array} imageIds
|
||||||
|
*/
|
||||||
prefetchImageIds(imageIds) {
|
prefetchImageIds(imageIds) {
|
||||||
const nonCachedImageIds = this.filterCachedImageIds(imageIds);
|
const nonCachedImageIds = this.filterCachedImageIds(imageIds);
|
||||||
const requestPoolManager = cornerstoneTools.requestPoolManager;
|
const imageLoadPoolManager = cornerstone.imageLoadPoolManager;
|
||||||
const requestType = 'prefetch';
|
|
||||||
const preventCache = false;
|
let requestFn;
|
||||||
const noop = () => {};
|
if (this.options.preventCache) {
|
||||||
|
requestFn = id => cornerstone.loadImage(id);
|
||||||
|
} else {
|
||||||
|
requestFn = id => cornerstone.loadAndCacheImage(id);
|
||||||
|
}
|
||||||
|
|
||||||
nonCachedImageIds.forEach(imageId => {
|
nonCachedImageIds.forEach(imageId => {
|
||||||
requestPoolManager.addRequest(
|
imageLoadPoolManager.addRequest(
|
||||||
{},
|
requestFn.bind(this, imageId),
|
||||||
imageId,
|
this.options.requestType,
|
||||||
requestType,
|
{
|
||||||
preventCache,
|
imageId,
|
||||||
noop,
|
}
|
||||||
noop
|
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
requestPoolManager.startGrabbing();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get study by cornerstone image instance.
|
||||||
|
*
|
||||||
|
* @param {object} image
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
getStudy(image) {
|
getStudy(image) {
|
||||||
const StudyInstanceUID = cornerstone.metaData.get(
|
const StudyInstanceUID = cornerstone.metaData.get(
|
||||||
'StudyInstanceUID',
|
'StudyInstanceUID',
|
||||||
image.imageId
|
image.imageId
|
||||||
);
|
);
|
||||||
return OHIF.viewer.Studies.find(
|
const studies = this.studies;
|
||||||
study => study.StudyInstanceUID === StudyInstanceUID
|
return studies.find(
|
||||||
|
study => study.getData().StudyInstanceUID === StudyInstanceUID
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get study series by cornerstone image instance.
|
||||||
|
*
|
||||||
|
* @param {object} study OHIF study instance
|
||||||
|
* @param {object} image cornerstone image instance object
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
getSeries(study, image) {
|
getSeries(study, image) {
|
||||||
const SeriesInstanceUID = cornerstone.metaData.get(
|
const SeriesInstanceUID = cornerstone.metaData.get(
|
||||||
'SeriesInstanceUID',
|
'SeriesInstanceUID',
|
||||||
image.imageId
|
image.imageId
|
||||||
);
|
);
|
||||||
const studyMetadata = OHIF.viewerbase.getStudyMetadata(study);
|
return study.getSeriesByUID(SeriesInstanceUID);
|
||||||
|
|
||||||
return studyMetadata.getSeriesByUID(SeriesInstanceUID);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get sop instance by cornerstone image instance.
|
||||||
|
*
|
||||||
|
* @param {array} series
|
||||||
|
* @param {object} image
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
getInstance(series, image) {
|
getInstance(series, image) {
|
||||||
const instanceMetadata = cornerstone.metaData.get(
|
const instanceMetadata = cornerstone.metaData.get(
|
||||||
'instance',
|
'instance',
|
||||||
@@ -121,7 +216,33 @@ export class StudyPrefetcher {
|
|||||||
return series.getInstanceByUID(instanceMetadata.SOPInstanceUID);
|
return series.getInstanceByUID(instanceMetadata.SOPInstanceUID);
|
||||||
}
|
}
|
||||||
|
|
||||||
getActiveDisplaySet(displaySets, instance) {
|
/**
|
||||||
|
* Returns the display set with given uid.
|
||||||
|
*
|
||||||
|
* @param {string} displaySetInstanceUID the display set instance uid
|
||||||
|
* @returns {object} displaySet
|
||||||
|
*/
|
||||||
|
getDisplaySetByUID(displaySetInstanceUID) {
|
||||||
|
let displaySet;
|
||||||
|
this.studies.forEach(study => {
|
||||||
|
const ds = study.displaySets.find(
|
||||||
|
ds => ds.displaySetInstanceUID === displaySetInstanceUID
|
||||||
|
);
|
||||||
|
if (ds) {
|
||||||
|
displaySet = ds;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return displaySet;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get display set by SOPInstanceUID.
|
||||||
|
*
|
||||||
|
* @param {array} displaySets
|
||||||
|
* @param {object} instance
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
getDisplaySetBySOPInstanceUID(displaySets, instance) {
|
||||||
return displaySets.find(displaySet => {
|
return displaySets.find(displaySet => {
|
||||||
return displaySet.images.some(displaySetImage => {
|
return displaySet.images.some(displaySetImage => {
|
||||||
return displaySetImage.SOPInstanceUID === instance.SOPInstanceUID;
|
return displaySetImage.SOPInstanceUID === instance.SOPInstanceUID;
|
||||||
@@ -129,25 +250,48 @@ export class StudyPrefetcher {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
getDisplaySetsToPrefetch(config) {
|
/**
|
||||||
|
* Get active viewport image based on cornerstone viewport element.
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
getActiveViewportImage() {
|
||||||
|
if (!this.enabledElement) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.enabledElement.image;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prefetch display sets based on cornerstone viewport element image.
|
||||||
|
*
|
||||||
|
* @param {string} displaySetInstanceUID the display set instance uid
|
||||||
|
* @returns {array} displaySets
|
||||||
|
*/
|
||||||
|
getDisplaySetsToPrefetch(displaySetInstanceUID) {
|
||||||
const image = this.getActiveViewportImage();
|
const image = this.getActiveViewportImage();
|
||||||
|
|
||||||
if (!image || !config || !config.displaySetCount) {
|
if (!image) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*const study = this.getStudy(image);
|
const study = this.getStudy(image);
|
||||||
const series = this.getSeries(study, image);
|
const series = this.getSeries(study, image);
|
||||||
const instance = this.getInstance(series, image);*/
|
const instance = this.getInstance(series, image);
|
||||||
const displaySets = study.displaySets;
|
const displaySets = study.displaySets;
|
||||||
const activeDisplaySet = null; //this.getActiveDisplaySet(displaySets, instance);
|
const activeDisplaySet = displaySetInstanceUID
|
||||||
|
? this.getDisplaySetByUID(displaySetInstanceUID)
|
||||||
|
: this.getDisplaySetBySOPInstanceUID(displaySets, instance);
|
||||||
|
|
||||||
const prefetchMethodMap = {
|
const prefetchMethodMap = {
|
||||||
topdown: 'getFirstDisplaySets',
|
topdown: 'getFirstDisplaySets',
|
||||||
downward: 'getNextDisplaySets',
|
downward: 'getNextDisplaySets',
|
||||||
|
upward: 'getPreviousDisplaySets',
|
||||||
closest: 'getClosestDisplaySets',
|
closest: 'getClosestDisplaySets',
|
||||||
|
all: 'getAllDisplaySets',
|
||||||
};
|
};
|
||||||
|
|
||||||
const prefetchOrder = config.order;
|
const prefetchOrder = this.options.order;
|
||||||
const methodName = prefetchMethodMap[prefetchOrder];
|
const methodName = prefetchMethodMap[prefetchOrder];
|
||||||
const getDisplaySets = this[methodName];
|
const getDisplaySets = this[methodName];
|
||||||
|
|
||||||
@@ -163,18 +307,59 @@ export class StudyPrefetcher {
|
|||||||
this,
|
this,
|
||||||
displaySets,
|
displaySets,
|
||||||
activeDisplaySet,
|
activeDisplaySet,
|
||||||
config.displaySetCount
|
this.options.displaySetCount,
|
||||||
|
this.options.includeActiveDisplaySet
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
getFirstDisplaySets(displaySets, activeDisplaySet, displaySetCount) {
|
/**
|
||||||
|
* Get all display sets.
|
||||||
|
*
|
||||||
|
* @param {array} displaySets
|
||||||
|
* @param {object} activeDisplaySet
|
||||||
|
* @param {number} displaySetCount
|
||||||
|
* @param {boolean} includeActiveDisplaySet
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
getAllDisplaySets(
|
||||||
|
displaySets,
|
||||||
|
activeDisplaySet,
|
||||||
|
displaySetCount,
|
||||||
|
includeActiveDisplaySet
|
||||||
|
) {
|
||||||
|
const length = displaySets.length;
|
||||||
|
const selectedDisplaySets = [];
|
||||||
|
|
||||||
|
for (let i = 0; i < length; i++) {
|
||||||
|
const displaySet = displaySets[i];
|
||||||
|
selectedDisplaySets.push(displaySet);
|
||||||
|
}
|
||||||
|
|
||||||
|
return selectedDisplaySets;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all display sets in order after the active display set.
|
||||||
|
*
|
||||||
|
* @param {array} displaySets
|
||||||
|
* @param {object} activeDisplaySet
|
||||||
|
* @param {number} displaySetCount
|
||||||
|
* @param {boolean} includeActiveDisplaySet
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
getFirstDisplaySets(
|
||||||
|
displaySets,
|
||||||
|
activeDisplaySet,
|
||||||
|
displaySetCount,
|
||||||
|
includeActiveDisplaySet
|
||||||
|
) {
|
||||||
const length = displaySets.length;
|
const length = displaySets.length;
|
||||||
const selectedDisplaySets = [];
|
const selectedDisplaySets = [];
|
||||||
|
|
||||||
for (let i = 0; i < length && displaySetCount; i++) {
|
for (let i = 0; i < length && displaySetCount; i++) {
|
||||||
const displaySet = displaySets[i];
|
const displaySet = displaySets[i];
|
||||||
|
|
||||||
if (displaySet !== activeDisplaySet) {
|
if (includeActiveDisplaySet || displaySet !== activeDisplaySet) {
|
||||||
selectedDisplaySets.push(displaySet);
|
selectedDisplaySets.push(displaySet);
|
||||||
displaySetCount--;
|
displaySetCount--;
|
||||||
}
|
}
|
||||||
@@ -183,21 +368,78 @@ export class StudyPrefetcher {
|
|||||||
return selectedDisplaySets;
|
return selectedDisplaySets;
|
||||||
}
|
}
|
||||||
|
|
||||||
getNextDisplaySets(displaySets, activeDisplaySet, displaySetCount) {
|
/**
|
||||||
|
* Get all display sets before the active display set.
|
||||||
|
*
|
||||||
|
* @param {array} displaySets
|
||||||
|
* @param {object} activeDisplaySet
|
||||||
|
* @param {number} displaySetCount
|
||||||
|
* @param {boolean} includeActiveDisplaySet
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
getPreviousDisplaySets(
|
||||||
|
displaySets,
|
||||||
|
activeDisplaySet,
|
||||||
|
displaySetCount,
|
||||||
|
includeActiveDisplaySet
|
||||||
|
) {
|
||||||
const activeDisplaySetIndex = displaySets.indexOf(activeDisplaySet);
|
const activeDisplaySetIndex = displaySets.indexOf(activeDisplaySet);
|
||||||
const begin = activeDisplaySetIndex + 1;
|
const end = includeActiveDisplaySet
|
||||||
const end = Math.min(begin + displaySetCount, displaySets.length);
|
? activeDisplaySetIndex + 1
|
||||||
|
: activeDisplaySetIndex;
|
||||||
|
const previousDisplaySets = displaySets.slice(0, end);
|
||||||
|
return previousDisplaySets.reverse().slice(0, displaySetCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all display sets after the active display set.
|
||||||
|
*
|
||||||
|
* @param {array} displaySets
|
||||||
|
* @param {object} activeDisplaySet
|
||||||
|
* @param {number} displaySetCount
|
||||||
|
* @param {boolean} includeActiveDisplaySet
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
getNextDisplaySets(
|
||||||
|
displaySets,
|
||||||
|
activeDisplaySet,
|
||||||
|
displaySetCount,
|
||||||
|
includeActiveDisplaySet
|
||||||
|
) {
|
||||||
|
const activeDisplaySetIndex = displaySets.indexOf(activeDisplaySet);
|
||||||
|
const begin = includeActiveDisplaySet
|
||||||
|
? activeDisplaySetIndex
|
||||||
|
: activeDisplaySetIndex + 1;
|
||||||
|
const end = Math.min(begin + displaySetCount, displaySets.length);
|
||||||
return displaySets.slice(begin, end);
|
return displaySets.slice(begin, end);
|
||||||
}
|
}
|
||||||
|
|
||||||
getClosestDisplaySets(displaySets, activeDisplaySet, displaySetCount) {
|
/**
|
||||||
|
* Get all display set closest to the active display set.
|
||||||
|
*
|
||||||
|
* @param {array} displaySets
|
||||||
|
* @param {object} activeDisplaySet
|
||||||
|
* @param {number} displaySetCount
|
||||||
|
* @param {boolean} includeActiveDisplaySet
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
getClosestDisplaySets(
|
||||||
|
displaySets,
|
||||||
|
activeDisplaySet,
|
||||||
|
displaySetCount,
|
||||||
|
includeActiveDisplaySet
|
||||||
|
) {
|
||||||
const activeDisplaySetIndex = displaySets.indexOf(activeDisplaySet);
|
const activeDisplaySetIndex = displaySets.indexOf(activeDisplaySet);
|
||||||
const length = displaySets.length;
|
const length = displaySets.length;
|
||||||
const selectedDisplaySets = [];
|
const selectedDisplaySets = [];
|
||||||
let left = activeDisplaySetIndex - 1;
|
let left = activeDisplaySetIndex - 1;
|
||||||
let right = activeDisplaySetIndex + 1;
|
let right = activeDisplaySetIndex + 1;
|
||||||
|
|
||||||
|
if (includeActiveDisplaySet) {
|
||||||
|
selectedDisplaySets.push(displaySets[activeDisplaySetIndex]);
|
||||||
|
displaySetCount--;
|
||||||
|
}
|
||||||
|
|
||||||
while ((left >= 0 || right < length) && displaySetCount) {
|
while ((left >= 0 || right < length) && displaySetCount) {
|
||||||
if (left >= 0) {
|
if (left >= 0) {
|
||||||
selectedDisplaySets.push(displaySets[left]);
|
selectedDisplaySets.push(displaySets[left]);
|
||||||
@@ -215,6 +457,12 @@ export class StudyPrefetcher {
|
|||||||
return selectedDisplaySets;
|
return selectedDisplaySets;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all image ids from display sets.
|
||||||
|
*
|
||||||
|
* @param {array} displaySets
|
||||||
|
* @returns {array} image ids
|
||||||
|
*/
|
||||||
getImageIdsFromDisplaySets(displaySets) {
|
getImageIdsFromDisplaySets(displaySets) {
|
||||||
let imageIds = [];
|
let imageIds = [];
|
||||||
|
|
||||||
@@ -225,9 +473,19 @@ export class StudyPrefetcher {
|
|||||||
return imageIds;
|
return imageIds;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all image ids from a given display set.
|
||||||
|
*
|
||||||
|
* @param {array} displaySet
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
getImageIdsFromDisplaySet(displaySet) {
|
getImageIdsFromDisplaySet(displaySet) {
|
||||||
const imageIds = [];
|
const imageIds = [];
|
||||||
|
|
||||||
|
if (!displaySet.images || displaySet.images.length < 1) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: This duplicates work done by the stack manager
|
// TODO: This duplicates work done by the stack manager
|
||||||
displaySet.images.forEach(image => {
|
displaySet.images.forEach(image => {
|
||||||
const numFrames = image.numFrames;
|
const numFrames = image.numFrames;
|
||||||
@@ -245,15 +503,30 @@ export class StudyPrefetcher {
|
|||||||
return imageIds;
|
return imageIds;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filter cached image ids from a set of image ids.
|
||||||
|
*
|
||||||
|
* @param {array} imageIds
|
||||||
|
* @returns {array} images not cached
|
||||||
|
*/
|
||||||
filterCachedImageIds(imageIds) {
|
filterCachedImageIds(imageIds) {
|
||||||
return imageIds.filter(imageId => !this.isImageCached(imageId));
|
return imageIds.filter(imageId => !this.isImageCached(imageId));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if image id is cached in cornerstone.
|
||||||
|
*
|
||||||
|
* @param {string} imageId
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
isImageCached(imageId) {
|
isImageCached(imageId) {
|
||||||
const image = cornerstone.imageCache.imageCache[imageId];
|
const image = cornerstone.imageCache.imageCache[imageId];
|
||||||
return image && image.sizeInBytes;
|
return image && image.sizeInBytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Warns that cache is full and stops prefetching.
|
||||||
|
*/
|
||||||
cacheFullHandler = () => {
|
cacheFullHandler = () => {
|
||||||
log.warn('Cache full');
|
log.warn('Cache full');
|
||||||
this.stopPrefetching();
|
this.stopPrefetching();
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ import CommandsManager from './CommandsManager.js';
|
|||||||
import { DICOMFileLoadingListener } from './StudyLoadingListener';
|
import { DICOMFileLoadingListener } from './StudyLoadingListener';
|
||||||
import HotkeysManager from './HotkeysManager.js';
|
import HotkeysManager from './HotkeysManager.js';
|
||||||
import ImageSet from './ImageSet';
|
import ImageSet from './ImageSet';
|
||||||
|
import LogManager from './LogManager';
|
||||||
|
import PubSub from './PubSub';
|
||||||
import MetadataProvider from './MetadataProvider';
|
import MetadataProvider from './MetadataProvider';
|
||||||
import OHIFError from './OHIFError.js';
|
import OHIFError from './OHIFError.js';
|
||||||
import { OHIFStudyMetadataSource } from './OHIFStudyMetadataSource';
|
import { OHIFStudyMetadataSource } from './OHIFStudyMetadataSource';
|
||||||
@@ -36,7 +38,9 @@ const classes = {
|
|||||||
MetadataProvider,
|
MetadataProvider,
|
||||||
CommandsManager,
|
CommandsManager,
|
||||||
HotkeysManager,
|
HotkeysManager,
|
||||||
|
LogManager,
|
||||||
ImageSet,
|
ImageSet,
|
||||||
|
PubSub,
|
||||||
StudyPrefetcher,
|
StudyPrefetcher,
|
||||||
StudyLoadingListener,
|
StudyLoadingListener,
|
||||||
StackLoadingListener,
|
StackLoadingListener,
|
||||||
|
|||||||
@@ -9,11 +9,15 @@ 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
|
||||||
@@ -158,41 +162,64 @@ export class StudyMetadata extends Metadata {
|
|||||||
// series into another display set.
|
// series into another display set.
|
||||||
const stackableInstances = [];
|
const stackableInstances = [];
|
||||||
series.forEachInstance(instance => {
|
series.forEachInstance(instance => {
|
||||||
|
let displaySet;
|
||||||
|
|
||||||
// All imaging modalities must have a valid value for SOPClassUID (x00080016) or Rows (x00280010)
|
// All imaging modalities must have a valid value for SOPClassUID (x00080016) or Rows (x00280010)
|
||||||
if (
|
if (
|
||||||
!isImage(instance.getTagValue('SOPClassUID')) &&
|
!isImage(instance.getTagValue('SOPClassUID')) &&
|
||||||
!instance.getTagValue('Rows')
|
!instance.getTagValue('Rows')
|
||||||
) {
|
) {
|
||||||
return;
|
// we set an empty display and we add a isModalitySupported variable to
|
||||||
}
|
// print a warning that the modality is not supported in the thumbnail.
|
||||||
|
const displaySet = new ImageSet([]);
|
||||||
let displaySet;
|
const seriesData = series.getData();
|
||||||
|
|
||||||
if (isMultiFrame(instance)) {
|
|
||||||
displaySet = makeDisplaySet(series, [instance]);
|
|
||||||
|
|
||||||
displaySet.setAttributes({
|
displaySet.setAttributes({
|
||||||
sopClassUIDs,
|
displaySetInstanceUID: displaySet.uid,
|
||||||
isClip: true,
|
SeriesDate: seriesData.SeriesDate,
|
||||||
|
SeriesTime: seriesData.SeriesTime,
|
||||||
SeriesInstanceUID: series.getSeriesInstanceUID(),
|
SeriesInstanceUID: series.getSeriesInstanceUID(),
|
||||||
StudyInstanceUID: study.getStudyInstanceUID(), // Include the study instance UID for drag/drop purposes
|
SeriesNumber: instance.getTagValue('SeriesNumber'),
|
||||||
numImageFrames: instance.getTagValue('NumberOfFrames'), // Override the default value of instances.length
|
SeriesDescription: instance.getTagValue('SeriesDescription'),
|
||||||
InstanceNumber: instance.getTagValue('InstanceNumber'), // Include the instance number
|
numImageFrames: instance.getTagValue('NumberOfFrames'),
|
||||||
AcquisitionDatetime: instance.getTagValue('AcquisitionDateTime'), // Include the acquisition datetime
|
frameRate: instance.getTagValue('FrameTime'),
|
||||||
});
|
Modality: instance.getTagValue('Modality'),
|
||||||
displaySets.push(displaySet);
|
isMultiFrame: false,
|
||||||
} else if (isSingleImageModality(instance.Modality)) {
|
|
||||||
displaySet = makeDisplaySet(series, [instance]);
|
|
||||||
displaySet.setAttributes({
|
|
||||||
sopClassUIDs,
|
|
||||||
StudyInstanceUID: study.getStudyInstanceUID(), // Include the study instance UID
|
StudyInstanceUID: study.getStudyInstanceUID(), // Include the study instance UID
|
||||||
SeriesInstanceUID: series.getSeriesInstanceUID(),
|
|
||||||
InstanceNumber: instance.getTagValue('InstanceNumber'), // Include the instance number
|
InstanceNumber: instance.getTagValue('InstanceNumber'), // Include the instance number
|
||||||
AcquisitionDatetime: instance.getTagValue('AcquisitionDateTime'), // Include the acquisition datetime
|
AcquisitionDatetime: instance.getTagValue('AcquisitionDateTime'), // Include the acquisition datetime
|
||||||
|
isReconstructable: false,
|
||||||
|
isModalitySupported: false,
|
||||||
|
metadata: instance.getData().metadata,
|
||||||
});
|
});
|
||||||
|
|
||||||
displaySets.push(displaySet);
|
displaySets.push(displaySet);
|
||||||
} else {
|
} else {
|
||||||
stackableInstances.push(instance);
|
if (isMultiFrame(instance)) {
|
||||||
|
displaySet = makeDisplaySet(series, [instance]);
|
||||||
|
|
||||||
|
displaySet.setAttributes({
|
||||||
|
sopClassUIDs,
|
||||||
|
isClip: true,
|
||||||
|
SeriesInstanceUID: series.getSeriesInstanceUID(),
|
||||||
|
StudyInstanceUID: study.getStudyInstanceUID(), // Include the study instance UID for drag/drop purposes
|
||||||
|
numImageFrames: instance.getTagValue('NumberOfFrames'), // Override the default value of instances.length
|
||||||
|
InstanceNumber: instance.getTagValue('InstanceNumber'), // Include the instance number
|
||||||
|
AcquisitionDatetime: instance.getTagValue('AcquisitionDateTime'), // Include the acquisition datetime
|
||||||
|
});
|
||||||
|
displaySets.push(displaySet);
|
||||||
|
} else if (isSingleImageModality(instance.Modality)) {
|
||||||
|
displaySet = makeDisplaySet(series, [instance]);
|
||||||
|
displaySet.setAttributes({
|
||||||
|
sopClassUIDs,
|
||||||
|
StudyInstanceUID: study.getStudyInstanceUID(), // Include the study instance UID
|
||||||
|
SeriesInstanceUID: series.getSeriesInstanceUID(),
|
||||||
|
InstanceNumber: instance.getTagValue('InstanceNumber'), // Include the instance number
|
||||||
|
AcquisitionDatetime: instance.getTagValue('AcquisitionDateTime'), // Include the acquisition datetime
|
||||||
|
});
|
||||||
|
displaySets.push(displaySet);
|
||||||
|
} else {
|
||||||
|
stackableInstances.push(instance);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -226,6 +253,60 @@ 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,19 +333,9 @@ export class StudyMetadata extends Metadata {
|
|||||||
if (referencedSeriesInstanceUID) {
|
if (referencedSeriesInstanceUID) {
|
||||||
filteredDerivedDisplaySets = filteredDerivedDisplaySets.filter(
|
filteredDerivedDisplaySets = filteredDerivedDisplaySets.filter(
|
||||||
displaySet => {
|
displaySet => {
|
||||||
if (!displaySet.metadata.ReferencedSeriesSequence) {
|
return (
|
||||||
return false;
|
StudyMetadata.getReferencedDisplaySet(displaySet, [this])
|
||||||
}
|
.SeriesInstanceUID === referencedSeriesInstanceUID
|
||||||
|
|
||||||
const ReferencedSeriesSequence = Array.isArray(
|
|
||||||
displaySet.metadata.ReferencedSeriesSequence
|
|
||||||
)
|
|
||||||
? displaySet.metadata.ReferencedSeriesSequence
|
|
||||||
: [displaySet.metadata.ReferencedSeriesSequence];
|
|
||||||
|
|
||||||
return ReferencedSeriesSequence.some(
|
|
||||||
ReferencedSeries =>
|
|
||||||
ReferencedSeries.SeriesInstanceUID === referencedSeriesInstanceUID
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@@ -273,8 +344,7 @@ export class StudyMetadata extends Metadata {
|
|||||||
if (referencedFrameOfReferenceUID) {
|
if (referencedFrameOfReferenceUID) {
|
||||||
filteredDerivedDisplaySets = filteredDerivedDisplaySets.filter(
|
filteredDerivedDisplaySets = filteredDerivedDisplaySets.filter(
|
||||||
displaySet =>
|
displaySet =>
|
||||||
displaySet.ReferencedFrameOfReferenceUID ===
|
displaySet.FrameOfReferenceUID === referencedFrameOfReferenceUID
|
||||||
referencedFrameOfReferenceUID
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -459,6 +529,14 @@ export class StudyMetadata extends Metadata {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
this._displaySets.some(
|
||||||
|
ds => ds.displaySetInstanceUID === displaySet.displaySetInstanceUID
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
this._displaySets.splice(insertIndex, 0, displaySet);
|
this._displaySets.splice(insertIndex, 0, displaySet);
|
||||||
this.displaySets = this._displaySets;
|
this.displaySets = this._displaySets;
|
||||||
}
|
}
|
||||||
@@ -767,6 +845,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 +874,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,20 +892,39 @@ 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
imageSet.isModalitySupported = true;
|
||||||
|
|
||||||
return imageSet;
|
return imageSet;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -878,6 +985,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 +1000,149 @@ 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',
|
||||||
|
|||||||
@@ -78,17 +78,6 @@ export const setActiveViewportSpecificData = viewportSpecificData => ({
|
|||||||
/**
|
/**
|
||||||
* NOT-VIEWPORT
|
* NOT-VIEWPORT
|
||||||
*/
|
*/
|
||||||
export const setStudyLoadingProgress = (progressId, progressData) => ({
|
|
||||||
type: 'SET_STUDY_LOADING_PROGRESS',
|
|
||||||
progressId,
|
|
||||||
progressData,
|
|
||||||
});
|
|
||||||
|
|
||||||
export const clearStudyLoadingProgress = progressId => ({
|
|
||||||
type: 'CLEAR_STUDY_LOADING_PROGRESS',
|
|
||||||
progressId,
|
|
||||||
});
|
|
||||||
|
|
||||||
export const setUserPreferences = state => ({
|
export const setUserPreferences = state => ({
|
||||||
type: SET_USER_PREFERENCES,
|
type: SET_USER_PREFERENCES,
|
||||||
state,
|
state,
|
||||||
@@ -134,8 +123,6 @@ const actions = {
|
|||||||
/**
|
/**
|
||||||
* NOT-VIEWPORT
|
* NOT-VIEWPORT
|
||||||
*/
|
*/
|
||||||
setStudyLoadingProgress,
|
|
||||||
clearStudyLoadingProgress,
|
|
||||||
setUserPreferences,
|
setUserPreferences,
|
||||||
setExtensionData,
|
setExtensionData,
|
||||||
setTimepoints,
|
setTimepoints,
|
||||||
|
|||||||
@@ -11,8 +11,6 @@ describe('actions', () => {
|
|||||||
'setLayout',
|
'setLayout',
|
||||||
'clearViewportSpecificData',
|
'clearViewportSpecificData',
|
||||||
'setActiveViewportSpecificData',
|
'setActiveViewportSpecificData',
|
||||||
'setStudyLoadingProgress',
|
|
||||||
'clearStudyLoadingProgress',
|
|
||||||
'setUserPreferences',
|
'setUserPreferences',
|
||||||
'setExtensionData',
|
'setExtensionData',
|
||||||
'setTimepoints',
|
'setTimepoints',
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import extensions from './extensions';
|
import extensions from './extensions';
|
||||||
import loading from './loading';
|
|
||||||
import preferences from './preferences';
|
import preferences from './preferences';
|
||||||
import servers from './servers';
|
import servers from './servers';
|
||||||
import studies from './studies';
|
import studies from './studies';
|
||||||
@@ -8,7 +7,6 @@ import viewports from './viewports';
|
|||||||
|
|
||||||
const reducers = {
|
const reducers = {
|
||||||
extensions,
|
extensions,
|
||||||
loading,
|
|
||||||
preferences,
|
preferences,
|
||||||
servers,
|
servers,
|
||||||
studies,
|
studies,
|
||||||
|
|||||||
@@ -1,33 +0,0 @@
|
|||||||
import cloneDeep from 'lodash.clonedeep';
|
|
||||||
|
|
||||||
const defaultState = {
|
|
||||||
progress: {},
|
|
||||||
lastUpdated: null,
|
|
||||||
};
|
|
||||||
|
|
||||||
const loading = (state = defaultState, action) => {
|
|
||||||
let progress;
|
|
||||||
let lastUpdated;
|
|
||||||
switch (action.type) {
|
|
||||||
case 'SET_STUDY_LOADING_PROGRESS':
|
|
||||||
progress = cloneDeep(state).progress;
|
|
||||||
progress[action.progressId] = action.progressData;
|
|
||||||
|
|
||||||
// This is a workaround so we can easily identify changes
|
|
||||||
// to the progress object without doing deep comparison.
|
|
||||||
lastUpdated = new Date().getTime();
|
|
||||||
|
|
||||||
return Object.assign({}, state, { progress, lastUpdated });
|
|
||||||
case 'CLEAR_STUDY_LOADING_PROGRESS':
|
|
||||||
progress = cloneDeep(state).progress;
|
|
||||||
delete progress[action.progressId];
|
|
||||||
|
|
||||||
lastUpdated = new Date().getTime();
|
|
||||||
|
|
||||||
return Object.assign({}, state, { progress, lastUpdated });
|
|
||||||
default:
|
|
||||||
return state;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export default loading;
|
|
||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
Loaded 100 of 326 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user