Compare commits
470
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7a69c46dc0 | ||
|
|
70367bad0b | ||
|
|
cd347812bf | ||
|
|
befd7d1b08 | ||
|
|
618b0fc516 | ||
|
|
693bfc5ac3 | ||
|
|
c14daf6735 | ||
|
|
5e35fbb21d | ||
|
|
efd02088f1 | ||
|
|
d7f2b3acaf | ||
|
|
c609acaaad | ||
|
|
4db092f4bf | ||
|
|
994abf2a6a | ||
|
|
c7db3fca24 | ||
|
|
ce8e00ea73 | ||
|
|
4d4356338c | ||
|
|
9d9e1aa4fe | ||
|
|
884224541b | ||
|
|
69a61698ae | ||
|
|
82b4f72408 | ||
|
|
c87d933bbc | ||
|
|
b1745c5157 | ||
|
|
096590a7e9 | ||
|
|
106b67d436 | ||
|
|
cefe3c3cfb | ||
|
|
2a2d5eefcb | ||
|
|
3f55e57056 | ||
|
|
30515b8ab5 | ||
|
|
24bbf2b190 | ||
|
|
cb9aa6a765 | ||
|
|
5d9e4c9b02 | ||
|
|
dab095b6b5 | ||
|
|
cfa697aabc | ||
|
|
3350b1faa6 | ||
|
|
4ffb50c7e9 | ||
|
|
1d29554a91 | ||
|
|
b9d484fb2e | ||
|
|
597ac11c4d | ||
|
|
6007f340bf | ||
|
|
44738078db | ||
|
|
686fe7d931 | ||
|
|
0b8facbea6 | ||
|
|
0f89aa32f9 | ||
|
|
545161d5e8 | ||
|
|
eaa423c64e | ||
|
|
f7db74a843 | ||
|
|
7aba01f534 | ||
|
|
c5ebeaa127 | ||
|
|
d7e1d2c975 | ||
|
|
eb7eb56d7e | ||
|
|
c5ee092bf2 | ||
|
|
e2f4f95222 | ||
|
|
4546ede9bb | ||
|
|
e0fb439e98 | ||
|
|
ebbc9e0d6d | ||
|
|
47a6130fd5 | ||
|
|
80dfdcc37c | ||
|
|
d74ad370e0 | ||
|
|
ddfde9279e | ||
|
|
aaab6d2622 | ||
|
|
d2921083e7 | ||
|
|
a6338ea266 | ||
|
|
fe9551b9b1 | ||
|
|
ef60156a47 | ||
|
|
850de2903a | ||
|
|
f26ed5539b | ||
|
|
21aeaa63c7 | ||
|
|
2ac0d3f017 | ||
|
|
efdbe8e7b4 | ||
|
|
67b70eaa98 | ||
|
|
5150ed9427 | ||
|
|
1bcb0acf9f | ||
|
|
5d6cf2a609 | ||
|
|
9f813e0ea0 | ||
|
|
49de44ba34 | ||
|
|
b04e186274 | ||
|
|
c59d66412f | ||
|
|
f8b4ccbb59 | ||
|
|
5426773ffc | ||
|
|
a507a00f59 | ||
|
|
cedeb756c0 | ||
|
|
64385485b6 | ||
|
|
32828d9a6e | ||
|
|
b94d4782d1 | ||
|
|
36b47ce3a3 | ||
|
|
fe57c00577 | ||
|
|
21a3bc2ded | ||
|
|
b6b09f22fc | ||
|
|
64a4d60e52 | ||
|
|
0ba796c73c | ||
|
|
cb7fafb1d6 | ||
|
|
7164d1a654 | ||
|
|
383a0ef167 | ||
|
|
56bd49a0cd | ||
|
|
755b283574 | ||
|
|
a8a4fcee40 | ||
|
|
9892320ffb | ||
|
|
28befb1ea6 | ||
|
|
c554c5fac2 | ||
|
|
6e3a58931a | ||
|
|
5beb12a7fc | ||
|
|
1953118c8a | ||
|
|
7807f849f2 | ||
|
|
b426256df5 | ||
|
|
c9b7e97f9c | ||
|
|
0fc7d93d73 | ||
|
|
33307d3cd2 | ||
|
|
c3ce909434 | ||
|
|
ad2c750503 | ||
|
|
cceb61f123 | ||
|
|
13bcba8c39 | ||
|
|
d2db026158 | ||
|
|
e4b0552db4 | ||
|
|
d7ba2fac69 | ||
|
|
761d54fbbf | ||
|
|
79548d2380 | ||
|
|
6a401c9e52 | ||
|
|
23376d279d | ||
|
|
4110b0422c | ||
|
|
ed501bb462 | ||
|
|
abf050e162 | ||
|
|
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 | ||
|
|
7c3ac387ef | ||
|
|
d6f79d9ddf | ||
|
|
9448993cf0 | ||
|
|
1c556b4cc3 | ||
|
|
94a9067fe3 | ||
|
|
cfba4df70e | ||
|
|
11c1d12872 | ||
|
|
91364db882 | ||
|
|
bea31d5fec | ||
|
|
48ecbc261a | ||
|
|
8bc467fb07 | ||
|
|
cffc44f0f4 | ||
|
|
ea0fc07e30 | ||
|
|
b8d241fa33 | ||
|
|
ccdfcd4a85 | ||
|
|
6cf72d1757 | ||
|
|
bb1cdbda3a | ||
|
|
fab122dd8f | ||
|
|
42803b0ae8 | ||
|
|
5a78da8ff6 | ||
|
|
0ef88e88eb | ||
|
|
a7b5f1847a | ||
|
|
5cb6e63b88 | ||
|
|
48eb4a9ec3 | ||
|
|
d9231fd431 | ||
|
|
e5b578bd8e | ||
|
|
f38979037d | ||
|
|
45ca88f5b8 | ||
|
|
a1dee16a3d | ||
|
|
6a37a2a661 | ||
|
|
a8caf4bba4 | ||
|
|
29dcc82e28 | ||
|
|
dbf0501031 | ||
|
|
ef28fa2ac1 | ||
|
|
779a7e0976 | ||
|
|
7f3ca1fc23 | ||
|
|
59ab68b4ed | ||
|
|
248bc0a64a | ||
|
|
5fdace1432 | ||
|
|
71b6b454a3 | ||
|
|
3b02a06f5e | ||
|
|
ebdcde1c4d | ||
|
|
829ac2b410 | ||
|
|
09745a0349 | ||
|
|
cd73dd3027 | ||
|
|
ca511edeff | ||
|
|
bb5f30ce2a | ||
|
|
87d5c68e71 | ||
|
|
0e87ab37c2 | ||
|
|
756b005fe1 | ||
|
|
3d86b5f876 | ||
|
|
2a647fd618 | ||
|
|
f8fc31bcac | ||
|
|
b7ab88b0f1 | ||
|
|
57a30d7c34 | ||
|
|
cd90665b0e | ||
|
|
98c657231e | ||
|
|
14da247318 | ||
|
|
6a77d6c778 | ||
|
|
8f1785446b | ||
|
|
41225eefc0 | ||
|
|
7772fee21a | ||
|
|
d870ac914a | ||
|
|
451f7eab92 | ||
|
|
029f8af4c7 | ||
|
|
b1b4c8c861 | ||
|
|
cdde2af224 | ||
|
|
4a2922dda0 | ||
|
|
b6b5a8414f | ||
|
|
e7cc735c03 | ||
|
|
6121bdcf63 | ||
|
|
da70079675 | ||
|
|
9089875ca7 | ||
|
|
b4627ecfa3 | ||
|
|
4ede8a525a | ||
|
|
2c23818c86 | ||
|
|
fa4b03d62d | ||
|
|
698e900b85 | ||
|
|
b7d47383c5 | ||
|
|
6123741765 | ||
|
|
ab25be3520 | ||
|
|
950a54c704 | ||
|
|
4293c073f0 | ||
|
|
62a2cd5ff3 | ||
|
|
2d827c2a49 | ||
|
|
a8305772d0 | ||
|
|
eab115e70c | ||
|
|
c02b232b0c | ||
|
|
f01640d009 | ||
|
|
63fd65690c | ||
|
|
944afe4a3e | ||
|
|
b1678ce639 | ||
|
|
39937cca08 | ||
|
|
f7b8b6a41c | ||
|
|
79fe442293 | ||
|
|
875238e5b0 | ||
|
|
d2d3ca2828 | ||
|
|
cebea832a3 | ||
|
|
c4f84b1174 | ||
|
|
f9fe4232e7 | ||
|
|
42c22df1b6 | ||
|
|
19a8e71317 | ||
|
|
fa95bae6ef | ||
|
|
8a20a0526e | ||
|
|
5b20d8f323 | ||
|
|
9ea37a2201 | ||
|
|
2a66e175df | ||
|
|
77a421cdc0 | ||
|
|
7033886697 | ||
|
|
256563d7ee | ||
|
|
f7162ce617 | ||
|
|
027ef4f93c | ||
|
|
f09ebbe2e8 | ||
|
|
51b4e60161 | ||
|
|
08de987711 | ||
|
|
20e366ffbd |
No files matched your search
+77
-46
@@ -13,11 +13,21 @@ version: 2.1
|
||||
##
|
||||
orbs:
|
||||
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'
|
||||
defaults: &defaults
|
||||
docker:
|
||||
- image: circleci/node:12.9.1
|
||||
- image: cimg/node:14.19
|
||||
environment:
|
||||
TERM: xterm # Enable colors in term
|
||||
QUICK_BUILD: true
|
||||
@@ -36,12 +46,12 @@ jobs:
|
||||
- checkout:
|
||||
post:
|
||||
- git fetch --all
|
||||
- restore_cache:
|
||||
name: Restore Yarn and Cypress Package Cache
|
||||
keys:
|
||||
# when lock file changes, use increasingly general patterns to restore cache
|
||||
- yarn-packages-{{ checksum "yarn.lock" }}
|
||||
- yarn-packages-
|
||||
# - restore_cache:
|
||||
# name: Restore Yarn and Cypress Package Cache
|
||||
# keys:
|
||||
# # when lock file changes, use increasingly general patterns to restore cache
|
||||
# - yarn-packages-{{ checksum "yarn.lock" }}
|
||||
# - yarn-packages-
|
||||
- run:
|
||||
name: Install Dependencies
|
||||
command: yarn install --frozen-lockfile
|
||||
@@ -95,18 +105,19 @@ jobs:
|
||||
post:
|
||||
- git fetch --all
|
||||
|
||||
- restore_cache:
|
||||
name: Restore Yarn and Cypress Package Cache
|
||||
keys:
|
||||
# when lock file changes, use increasingly general patterns to restore cache
|
||||
- yarn-packages-{{ checksum "yarn.lock" }}
|
||||
- yarn-packages-
|
||||
# - restore_cache:
|
||||
# name: Restore Yarn and Cypress Package Cache
|
||||
# keys:
|
||||
# # when lock file changes, use increasingly general patterns to restore cache
|
||||
# - yarn-packages-{{ checksum "yarn.lock" }}
|
||||
# - yarn-packages-
|
||||
|
||||
- run:
|
||||
name: Install Dependencies
|
||||
command: yarn install --frozen-lockfile
|
||||
|
||||
- setup_remote_docker:
|
||||
version: 19.03.13
|
||||
docker_layer_caching: false
|
||||
|
||||
- run:
|
||||
@@ -131,12 +142,12 @@ jobs:
|
||||
- checkout:
|
||||
post:
|
||||
- git fetch --all
|
||||
- restore_cache:
|
||||
name: Restore Yarn and Cypress Package Cache
|
||||
keys:
|
||||
# when lock file changes, use increasingly general patterns to restore cache
|
||||
- yarn-packages-{{ checksum "yarn.lock" }}
|
||||
- yarn-packages-
|
||||
# - restore_cache:
|
||||
# name: Restore Yarn and Cypress Package Cache
|
||||
# keys:
|
||||
# # when lock file changes, use increasingly general patterns to restore cache
|
||||
# - yarn-packages-{{ checksum "yarn.lock" }}
|
||||
# - yarn-packages-
|
||||
- run:
|
||||
name: Install Dependencies
|
||||
command: yarn install --frozen-lockfile
|
||||
@@ -170,7 +181,7 @@ jobs:
|
||||
|
||||
DEPLOY_TO_DEV:
|
||||
docker:
|
||||
- image: circleci/node:12.9.1
|
||||
- image: circleci/node:14.15.0
|
||||
environment:
|
||||
TERM: xterm
|
||||
NETLIFY_SITE_ID: 32708787-c9b0-4634-b50f-7ca41952da77
|
||||
@@ -185,7 +196,7 @@ jobs:
|
||||
|
||||
DEPLOY_TO_STAGING:
|
||||
docker:
|
||||
- image: circleci/node:12.9.1
|
||||
- image: circleci/node:14.15.0
|
||||
environment:
|
||||
TERM: xterm
|
||||
NETLIFY_SITE_ID: c7502ae3-b150-493c-8422-05701e44a969
|
||||
@@ -200,7 +211,7 @@ jobs:
|
||||
|
||||
DEPLOY_TO_PRODUCTION:
|
||||
docker:
|
||||
- image: circleci/node:12.9.1
|
||||
- image: circleci/node:14.15.0
|
||||
environment:
|
||||
TERM: xterm
|
||||
NETLIFY_SITE_ID: 79c4a5da-5c95-4dc9-84f7-45fd9dfe21b0
|
||||
@@ -225,11 +236,11 @@ jobs:
|
||||
post:
|
||||
- git fetch --all
|
||||
# Use increasingly general patterns to restore cache
|
||||
- restore_cache:
|
||||
name: Restore Yarn and Cypress Package Cache
|
||||
keys:
|
||||
- yarn-packages-{{ checksum "yarn.lock" }}
|
||||
- yarn-packages-
|
||||
# - restore_cache:
|
||||
# name: Restore Yarn and Cypress Package Cache
|
||||
# keys:
|
||||
# - yarn-packages-{{ checksum "yarn.lock" }}
|
||||
# - yarn-packages-
|
||||
- run:
|
||||
name: Install Dependencies
|
||||
command: yarn install --frozen-lockfile
|
||||
@@ -257,7 +268,12 @@ jobs:
|
||||
paths: .
|
||||
|
||||
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:
|
||||
- checkout
|
||||
- run:
|
||||
@@ -278,6 +294,7 @@ jobs:
|
||||
- attach_workspace:
|
||||
at: ~/repo
|
||||
- setup_remote_docker:
|
||||
version: 19.03.13
|
||||
docker_layer_caching: false
|
||||
- run:
|
||||
name: Build and push Docker image
|
||||
@@ -317,16 +334,21 @@ workflows:
|
||||
# E2E: PWA
|
||||
- cypress/run:
|
||||
name: 'E2E: PWA'
|
||||
executor: cypress/browsers-chrome76
|
||||
executor: chrome-and-pacs
|
||||
browser: chrome
|
||||
pre-steps:
|
||||
- run: 'rm -rf ~/.yarn && npm i -g yarn && yarn -v && yarn global
|
||||
add wait-on' # Use yarn latest
|
||||
- run: |
|
||||
# Clear yarn cache; update to latest
|
||||
rm -rf ~/.yarn
|
||||
npm i -g yarn
|
||||
yarn -v
|
||||
yarn: true
|
||||
record: false
|
||||
store_artifacts: false
|
||||
record: true
|
||||
store_artifacts: true
|
||||
working_directory: platform/viewer
|
||||
build: npx cross-env QUICK_BUILD=true yarn run build
|
||||
build:
|
||||
git submodule update --init && npx cross-env QUICK_BUILD=true
|
||||
APP_CONFIG=config/e2e.js yarn run build
|
||||
start: yarn run test:e2e:serve
|
||||
spec: 'cypress/integration/common/**/*,cypress/integration/pwa/**/*'
|
||||
wait-on: 'http://localhost:3000'
|
||||
@@ -337,21 +359,24 @@ workflows:
|
||||
path: platform/viewer/cypress/screenshots
|
||||
- store_artifacts:
|
||||
path: platform/viewer/cypress/videos
|
||||
- store_test_results:
|
||||
path: platform/viewer/cypress/results
|
||||
requires:
|
||||
- UNIT_TESTS
|
||||
# E2E: script-tag
|
||||
- cypress/run:
|
||||
name: 'E2E: Script Tag'
|
||||
executor: cypress/browsers-chrome76
|
||||
executor: chrome-and-pacs
|
||||
browser: chrome
|
||||
pre-steps:
|
||||
- run: 'rm -rf ~/.yarn && npm i -g yarn && yarn -v && yarn global
|
||||
add wait-on' # Use yarn latest
|
||||
- run: 'rm -rf ~/.yarn && npm i -g yarn && yarn -v' # Use yarn latest
|
||||
yarn: true
|
||||
record: false
|
||||
store_artifacts: false
|
||||
record: true
|
||||
store_artifacts: true
|
||||
working_directory: platform/viewer
|
||||
build: npx cross-env QUICK_BUILD=true yarn run build:package
|
||||
build:
|
||||
git submodule update --init && npx cross-env QUICK_BUILD=true
|
||||
APP_CONFIG=config/e2e.js yarn run build:package
|
||||
start: yarn run test:e2e:serve
|
||||
spec: 'cypress/integration/common/**/*,cypress/integration/script-tag/**/*'
|
||||
wait-on: 'http://localhost:3000'
|
||||
@@ -362,6 +387,8 @@ workflows:
|
||||
path: platform/viewer/cypress/screenshots
|
||||
- store_artifacts:
|
||||
path: platform/viewer/cypress/videos
|
||||
- store_test_results:
|
||||
path: platform/viewer/cypress/results
|
||||
requires:
|
||||
- UNIT_TESTS
|
||||
|
||||
@@ -372,7 +399,7 @@ workflows:
|
||||
# Update hub.docker.org
|
||||
- cypress/run:
|
||||
name: 'Generate Percy Snapshots'
|
||||
executor: cypress/browsers-chrome76
|
||||
executor: chrome-and-pacs
|
||||
browser: chrome
|
||||
pre-steps:
|
||||
- run: 'rm -rf ~/.yarn && npm i -g yarn && yarn -v && yarn global
|
||||
@@ -380,7 +407,9 @@ workflows:
|
||||
yarn: true
|
||||
store_artifacts: false
|
||||
working_directory: platform/viewer
|
||||
build: npx cross-env QUICK_BUILD=true yarn run build
|
||||
build:
|
||||
git submodule update --init && npx cross-env QUICK_BUILD=true
|
||||
APP_CONFIG=config/e2e.js yarn run build
|
||||
# start server --> verify running --> percy + chrome + cypress
|
||||
command: yarn run test:e2e:dist
|
||||
cache-key: 'yarn-packages-{{ checksum "yarn.lock" }}'
|
||||
@@ -449,7 +478,7 @@ workflows:
|
||||
# and record a Cypress dashboard test run
|
||||
- cypress/run:
|
||||
name: 'Generate Percy Snapshots'
|
||||
executor: cypress/browsers-chrome76
|
||||
executor: chrome-and-pacs
|
||||
browser: chrome
|
||||
pre-steps:
|
||||
- run: 'rm -rf ~/.yarn && npm i -g yarn && yarn -v && yarn global
|
||||
@@ -457,7 +486,9 @@ workflows:
|
||||
yarn: true
|
||||
store_artifacts: false
|
||||
working_directory: platform/viewer
|
||||
build: npx cross-env QUICK_BUILD=true yarn run build
|
||||
build:
|
||||
git submodule update --init && npx cross-env QUICK_BUILD=true
|
||||
APP_CONFIG=config/e2e.js yarn run build
|
||||
# start server --> verify running --> percy + chrome + cypress
|
||||
command: yarn run test:e2e:dist
|
||||
cache-key: 'yarn-packages-{{ checksum "yarn.lock" }}'
|
||||
|
||||
@@ -28,6 +28,12 @@ if [ -n "$CLIENT_ID" ] || [ -n "$HEALTHCARE_API_ENDPOINT" ]
|
||||
cp /usr/share/nginx/html/google.js /usr/share/nginx/html/app-config.js
|
||||
fi
|
||||
|
||||
if [ -n "${PORT}" ]
|
||||
then
|
||||
echo "Changing port to ${PORT}..."
|
||||
sed -i -e "s/listen 80/listen ${PORT}/g" /etc/nginx/conf.d/default.conf
|
||||
fi
|
||||
|
||||
echo "Starting Nginx to serve the OHIF Viewer..."
|
||||
|
||||
exec "$@"
|
||||
@@ -0,0 +1 @@
|
||||
custom: https://giving.massgeneral.org/ohif
|
||||
@@ -2,23 +2,21 @@
|
||||
name: "\U0001F41B Bug report"
|
||||
about: Create a report to help us improve
|
||||
title: ''
|
||||
labels: 'Community: Report :bug:, Awaiting Reproduction, Triage :white_flag:'
|
||||
labels: 'Community: Report :bug:, Awaiting Reproduction'
|
||||
assignees: ''
|
||||
---
|
||||
|
||||
> **Before Creating an issue**
|
||||
>
|
||||
> - Are you running the latest version?
|
||||
> - Are you reporting to the correct repository?
|
||||
> - Did you search existing issues?
|
||||
|
||||
## Bug Report
|
||||
<!-- ⚠️⚠️ Do Not Delete This! bug_report_template ⚠️⚠️ -->
|
||||
<!-- Please read our Rules of Conduct: https://github.com/OHIF/Viewers/blob/master/CODE_OF_CONDUCT.md -->
|
||||
<!-- 🕮 Read our guide about our process: https://v3-docs.ohif.org/development/our-process -->
|
||||
<!-- 🔎 Search existing issues to avoid creating duplicates. -->
|
||||
<!-- 🧪 Test using the latest version -->
|
||||
|
||||
### Describe the Bug
|
||||
|
||||
_A clear and concise description of what the bug is._
|
||||
|
||||
### What steps can we follow to reproduce the bug?
|
||||
### Steps to Reproduce:
|
||||
|
||||
1. First step
|
||||
2. Second step
|
||||
@@ -28,6 +26,15 @@ _A clear and concise description of what the bug is._
|
||||
Please use code blocks to show formatted errors or code snippets
|
||||
```
|
||||
|
||||
### The current behavior
|
||||
|
||||
_A clear and concise description of what happens instead of the expected
|
||||
behavior._
|
||||
|
||||
### The expected behavior
|
||||
|
||||
_A clear and concise description of what you expected to happen._
|
||||
|
||||
> :warning: Reports we cannot reproduce are at risk of being marked stale and
|
||||
> closed. The more information you can provide, the more likely we are to look
|
||||
> into and address your issue.
|
||||
@@ -2,19 +2,28 @@
|
||||
name: "\U0001F680 Feature request"
|
||||
about: Suggest an idea for this project
|
||||
title: ''
|
||||
labels: 'Community: Request :hand:, Triage :white_flag:'
|
||||
labels: 'Community: Request :hand:'
|
||||
assignees: ''
|
||||
---
|
||||
|
||||
> :hand: Many people requests features. Tell us why yours is important to the
|
||||
<!-- ⚠️⚠️ Do Not Delete This! feature_request_template ⚠️⚠️ -->
|
||||
<!-- Please read our Rules of Conduct: https://github.com/OHIF/Viewers/blob/master/CODE_OF_CONDUCT.md -->
|
||||
|
||||
<!-- :hand: Many people requests features. Tell us why yours is important to the
|
||||
> community. How does it add value? Why _this feature_?
|
||||
>
|
||||
> Is your request very specific to your needs? Consider
|
||||
> [contributing it](https://docs.ohif.org/contributing.html) yourself! Or reach
|
||||
> out to a community member that offers
|
||||
> [contributing it](https://v3-docs.ohif.org/development/contributing) yourself!
|
||||
> Or reach out to a community member that offers
|
||||
> [consulting services](https://docs.ohif.org/help.html#paid--commercial).
|
||||
-->
|
||||
|
||||
## Request
|
||||
### Request
|
||||
|
||||
<!--
|
||||
Please include as much detail as possible including possibly mock up screen shots, workflow or logic flow diagrams etc.
|
||||
Discuss if and how the requested feature interacts with existing features
|
||||
-->
|
||||
|
||||
**What feature or change would you like to see made?**
|
||||
|
||||
|
||||
@@ -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.
|
||||
@@ -1,14 +1,91 @@
|
||||
### PR Checklist
|
||||
|
||||
- [ ] Brief description of changes
|
||||
- [ ] Links to any relevant issues
|
||||
- [ ] Required status checks are passing
|
||||
- [ ] User cases if changes impact the user's experience
|
||||
- [ ] `@mention` a maintainer to request a review
|
||||
<!-- Do Not Delete This! pr_template -->
|
||||
<!-- Please read our Rules of Conduct: https://github.com/OHIF/Viewers/blob/master/CODE_OF_CONDUCT.md -->
|
||||
<!-- 🕮 Read our guide about our Contributing Guide here https://v3-docs.ohif.org/development/contributing -->
|
||||
<!-- :hand: Thank you for starting this amazing contribution! -->
|
||||
|
||||
<!--
|
||||
Links
|
||||
-->
|
||||
⚠️⚠️ Please make sure the checklist section below is complete before submitting your PR.
|
||||
To complete the checklist, add an 'x' to each item: [] -> [x]
|
||||
(PRs that do not have all the checkboxes marked will not be approved)
|
||||
-->
|
||||
|
||||
### Context
|
||||
|
||||
<!--
|
||||
Provide a clear explanation of the reasoning behind this change, such as:
|
||||
- A link to the issue being addressed, using the format "Fixes #ISSUE_NUMBER"
|
||||
- An image showing the issue or problem being addressed (if not already in the issue)
|
||||
- Error logs or callStacks to help with the understanding of the problem (if not already in the issue)
|
||||
-->
|
||||
|
||||
### Changes & Results
|
||||
|
||||
<!--
|
||||
List all the changes that have been done, such as:
|
||||
- Add new components
|
||||
- Remove old components
|
||||
- Update dependencies
|
||||
|
||||
What are the effects of this change?
|
||||
- Before vs After
|
||||
- Screenshots / GIFs / Videos
|
||||
-->
|
||||
|
||||
### Testing
|
||||
|
||||
<!--
|
||||
Describe how we can test your changes.
|
||||
- open a URL
|
||||
- visit a page
|
||||
- click on a button
|
||||
- etc.
|
||||
-->
|
||||
|
||||
### Checklist
|
||||
|
||||
#### PR
|
||||
|
||||
<!--
|
||||
https://semantic-release.gitbook.io/semantic-release/#how-does-it-work
|
||||
|
||||
Examples:
|
||||
Please note the letter casing in the provided examples (upper or lower).
|
||||
|
||||
- feat(MeasurementService): add ...
|
||||
- fix(Toolbar): fix ...
|
||||
- docs(Readme): update ...
|
||||
- style(Whitespace): fix ...
|
||||
- refactor(ExtensionManager): ...
|
||||
- test(HangingProtocol): Add test ...
|
||||
- chore(git): update ...
|
||||
- perf(VolumeLoader): ...
|
||||
|
||||
You don't need to have each commit within the Pull Request follow the rule,
|
||||
but the PR title must comply with it, as it will be used as the commit message
|
||||
after the commits are squashed.
|
||||
-->
|
||||
|
||||
- [] My Pull Request title is descriptive, accurate and follows the
|
||||
semantic-release format and guidelines.
|
||||
|
||||
#### Code
|
||||
|
||||
- [] My code has been well-documented (function documentation, inline comments,
|
||||
etc.)
|
||||
|
||||
#### Public Documentation Updates
|
||||
|
||||
<!-- https://v3-docs.ohif.org/ -->
|
||||
|
||||
- [] The documentation page has been updated as necessary for any public API
|
||||
additions or removals.
|
||||
|
||||
#### Tested Environment
|
||||
|
||||
- [] "OS: <!--[e.g. Windows 10, macOS 10.15.4]"-->
|
||||
- [] "Node version: <!--[e.g. 16.14.0]"-->
|
||||
- [] "Browser:
|
||||
<!--[e.g. Chrome 83.0.4103.116, Firefox 77.0.1, Safari 13.1.1]"-->
|
||||
|
||||
<!-- prettier-ignore-start -->
|
||||
[blog]: https://circleci.com/blog/triggering-trusted-ci-jobs-on-untrusted-forks/
|
||||
|
||||
+4
-10
@@ -2,25 +2,19 @@
|
||||
# https://github.com/apps/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
|
||||
daysUntilClose: 7
|
||||
daysUntilClose: 60
|
||||
# Issues with these labels will never be considered stale
|
||||
exemptLabels:
|
||||
- 'Story :raised_hands:'
|
||||
- 'Bug: Verified :bug:'
|
||||
- 'Task: CI/Tooling :robot:'
|
||||
- 'Task: Docs 📖'
|
||||
- 'Task: Docs :book:'
|
||||
- 'Task: Refactor :hammer_and_wrench:'
|
||||
- 'Task: Tests :microscope:'
|
||||
- 'PR: Awaiting Review 👀'
|
||||
- 'Triage :white_flag:'
|
||||
- 'Extension: Discussion'
|
||||
- 'Announcement 🎉'
|
||||
- 'IDC:priority'
|
||||
- 'IDC:candidate'
|
||||
- 'IDC:collaboration'
|
||||
- 'Community: Request :hand:'
|
||||
- 'Community: Report :bug:'
|
||||
# Label to use when marking an issue as stale
|
||||
staleLabel: 'Stale :baguette_bread:'
|
||||
# Comment to post when marking an issue as stale. Set to `false` to disable
|
||||
|
||||
+4
-1
@@ -8,7 +8,10 @@ docs/_book
|
||||
src/version.js
|
||||
junit.xml
|
||||
coverage/
|
||||
.docz/
|
||||
*.js.map
|
||||
.yarn
|
||||
platform/docs/.docusaurus
|
||||
platform/ui/storybook-static
|
||||
|
||||
# YALC (for Erik)
|
||||
.yalc
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
[submodule "testdata"]
|
||||
path = testdata
|
||||
url = https://github.com/OHIF/viewer-testdata-dicomweb.git
|
||||
@@ -11,6 +11,7 @@ node -v
|
||||
|
||||
# Install build deps and all monorepo package dependencies. Yarn Workspaces
|
||||
# should also symlink all projects appropriately
|
||||
yarn run lerna:restore
|
||||
yarn install --no-ignore-optional --pure-lockfile
|
||||
|
||||
# Build && Move PWA Output
|
||||
@@ -21,6 +22,9 @@ mv platform/viewer/dist/* .netlify/www/pwa -v
|
||||
# Build && Move script output
|
||||
# yarn run build:package
|
||||
|
||||
# Cache all of the node_module dependencies in
|
||||
# extensions, modules, and platform packages
|
||||
yarn run lerna:cache
|
||||
echo 'Nothing left to see here. Go home, folks.'
|
||||
|
||||
# Build using react-scripts
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
14.18.0
|
||||
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 webpack = require('webpack');
|
||||
const PACKAGE = require('../platform/viewer/package.json');
|
||||
// ~~ RULES
|
||||
const loadShadersRule = require('./rules/loadShaders.js');
|
||||
const loadWebWorkersRule = require('./rules/loadWebWorkers.js');
|
||||
@@ -29,7 +28,7 @@ module.exports = (env, argv, { SRC_DIR, DIST_DIR }) => {
|
||||
|
||||
const config = {
|
||||
mode: isProdBuild ? 'production' : 'development',
|
||||
devtool: isProdBuild ? 'source-map' : 'cheap-module-eval-source-map',
|
||||
devtool: isProdBuild ? 'source-map' : 'eval-cheap-module-source-map',
|
||||
entry: {
|
||||
app: `${SRC_DIR}/index.js`,
|
||||
},
|
||||
@@ -77,7 +76,10 @@ module.exports = (env, argv, { SRC_DIR, DIST_DIR }) => {
|
||||
'process.env.DEBUG': JSON.stringify(process.env.DEBUG),
|
||||
'process.env.APP_CONFIG': JSON.stringify(process.env.APP_CONFIG || ''),
|
||||
'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),
|
||||
/* i18n */
|
||||
'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"
|
||||
+4
-3
@@ -21,7 +21,7 @@
|
||||
|
||||
# Stage 1: Build the application
|
||||
# docker build -t ohif/viewer:latest .
|
||||
FROM node:10.16.3-slim as builder
|
||||
FROM node:14-slim as builder
|
||||
|
||||
RUN mkdir /usr/src/app
|
||||
WORKDIR /usr/src/app
|
||||
@@ -39,9 +39,10 @@ COPY package.json /usr/src/app/package.json
|
||||
COPY postcss.config.js /usr/src/app/postcss.config.js
|
||||
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 yarn config set workspaces-experimental true
|
||||
RUN yarn install
|
||||
RUN yarn install --verbose
|
||||
|
||||
ENV PATH /usr/src/app/node_modules/.bin:$PATH
|
||||
ENV QUICK_BUILD true
|
||||
@@ -52,7 +53,7 @@ RUN yarn run build
|
||||
|
||||
# Stage 2: Bundle the built application into a Docker container
|
||||
# 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 rm -rf /etc/nginx/conf.d
|
||||
COPY .docker/Viewer-v2.x /etc/nginx/conf.d
|
||||
|
||||
@@ -121,8 +121,7 @@ window.config = {
|
||||
};
|
||||
```
|
||||
|
||||
- Install the viewer:
|
||||
`window.OHIFStandaloneViewer.installViewer(window.config);`
|
||||
- Install the viewer: `window.OHIFViewer.installViewer(window.config);`
|
||||
|
||||
This exact setup is demonstrated in this
|
||||
[CodeSandbox](https://codesandbox.io/s/viewer-script-tag-tprch) and in our
|
||||
@@ -259,6 +258,33 @@ Cancer Institute, Informatics Technology for Cancer Research (ITCR) program,
|
||||
under a
|
||||
[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.
|
||||
|
||||
### Research notice
|
||||
Please note that this repository is participating in a study into sustainability
|
||||
of open source projects. Data will be gathered about this repository for
|
||||
approximately the next 12 months, starting from June 2021.
|
||||
|
||||
Data collected will include number of contributors, number of PRs, time taken to
|
||||
close/merge these PRs, and issues closed.
|
||||
|
||||
For more information, please visit
|
||||
[our informational page](https://sustainable-open-science-and-software.github.io/) or download our [participant information sheet](https://sustainable-open-science-and-software.github.io/assets/PIS_sustainable_software.pdf).
|
||||
|
||||
## License
|
||||
|
||||
MIT © [OHIF](https://github.com/OHIF)
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
"-fontsettings",
|
||||
"github",
|
||||
"ga",
|
||||
"sitemap",
|
||||
"anchors",
|
||||
"versions"
|
||||
],
|
||||
@@ -24,7 +23,8 @@
|
||||
"token": "UA-110573590-2"
|
||||
},
|
||||
"sitemap": {
|
||||
"hostname": "https://docs.ohif.org"
|
||||
"hostname": "https://docs.ohif.org",
|
||||
"todo": "sitemap plugin removed; causing build errors"
|
||||
},
|
||||
"versions": {
|
||||
"gitbookConfigURL": "https://raw.githubusercontent.com/OHIF/Viewers/master/docs/book.json",
|
||||
|
||||
@@ -127,10 +127,107 @@ appropriate headers. You can find an example of this setup in our
|
||||
|
||||
#### What if my archive doesn't support DicomWeb?
|
||||
|
||||
> This is possible to do with the OHIF Viewer, but not as straightforward. Look
|
||||
> out for documentation on this subject in the near future.
|
||||
It's possible to supply all Study data via JSON format, in the event you do not have a DicomWeb endpoint.
|
||||
You can host all of the relevant files on any web accessible server (Amazon S3, Azure Blob Storage, Local file server etc.)
|
||||
|
||||
This JSON is supplied via the '?url=' query parameter.
|
||||
It should reference an endpoint that returns **application/json** formatted text.
|
||||
|
||||
If you do not have an API, you can simply return a text file containing the JSON from any web server.
|
||||
|
||||
|
||||
You tell the OHIF viewer to use JSON by appending the `'?url='` query to the `/Viewer` route:
|
||||
|
||||
eg. `https://my-test-ohif-server/viewer?url=https://my-json-server/study-uid.json`
|
||||
|
||||
The returned JSON object must contain a single root object with a 'studies' array.
|
||||
|
||||
|
||||
*Sample JSON format:*
|
||||
```JSON
|
||||
{
|
||||
"studies": [
|
||||
{
|
||||
"StudyInstanceUID": "1.2.840.113619.2.5.1762583153.215519.978957063.78",
|
||||
"StudyDescription": "BRAIN SELLA",
|
||||
"StudyDate": "20010108",
|
||||
"StudyTime": "120022",
|
||||
"PatientName": "MISTER^MR",
|
||||
"PatientId": "832040",
|
||||
"series": [
|
||||
{
|
||||
"SeriesDescription": "SAG T-1",
|
||||
"SeriesInstanceUID": "1.2.840.113619.2.5.1762583153.215519.978957063.121",
|
||||
"SeriesNumber": 2,
|
||||
"SeriesDate": "20010108",
|
||||
"SeriesTime": "120318",
|
||||
"Modality": "MR",
|
||||
"instances": [
|
||||
{
|
||||
"metadata": {
|
||||
"Columns": 512,
|
||||
"Rows": 512,
|
||||
"InstanceNumber": 3,
|
||||
"AcquisitionNumber": 0,
|
||||
"PhotometricInterpretation": "MONOCHROME2",
|
||||
"BitsAllocated": 16,
|
||||
"BitsStored": 16,
|
||||
"PixelRepresentation": 1,
|
||||
"SamplesPerPixel": 1,
|
||||
"PixelSpacing": [0.390625, 0.390625],
|
||||
"HighBit": 15,
|
||||
"ImageOrientationPatient": [0,1,0,0,0,-1],
|
||||
"ImagePositionPatient": [11.600000,-92.500000, 98.099998],
|
||||
"FrameOfReferenceUID": "1.2.840.113619.2.5.1762583153.223134.978956938.470",
|
||||
"ImageType": ["ORIGINAL","PRIMARY","OTHER"],
|
||||
"Modality": "MR",
|
||||
"SOPInstanceUID": "1.2.840.113619.2.5.1762583153.215519.978957063.124",
|
||||
"SeriesInstanceUID": "1.2.840.113619.2.5.1762583153.215519.978957063.121",
|
||||
"StudyInstanceUID": "1.2.840.113619.2.5.1762583153.215519.978957063.78"
|
||||
},
|
||||
"url": "dicomweb://s3.amazonaws.com/lury/MRStudy/1.2.840.113619.2.5.1762583153.215519.978957063.124.dcm"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
More info on this JSON format can be found here [Issue #1500](https://github.com/OHIF/Viewers/issues/1500)
|
||||
|
||||
|
||||
**Implementation Notes:**
|
||||
|
||||
1. When hosting the viewer, you will also need to host a /viewer route on the server - or the browser may not be able to find the route.
|
||||
2. For each instance url (dicom object) in the returned JSON, you must prefix the `url` with `dicomweb:` in order for the cornerstone image loader to retrieve it correctly.
|
||||
eg. `https://image-server/my-image.dcm` ---> `dicomweb:https://image-server/my-image.dcm`
|
||||
3. The JSON format above is compatible with >= v3.7.8 of the application. Older versions of the viewer used a different JSON format. As of 20/04/20 the public [https://viewer.ohif.org/] is a pre 3.0 version that does not support this format yet.
|
||||
4. The JSON format is case-sensitive. Please ensure you have matched casing with the naturalised Dicom format referenced in [Issue #1500](https://github.com/OHIF/Viewers/issues/1500).
|
||||
|
||||
*CORS Issues (Cross-Origin Resource Sharing)*
|
||||
|
||||
If you host a JSON API or Images on a different domain from the the app itself, you will likely have CORS issues. This will also happen when testing from Localhost and reaching out to remote servers.
|
||||
Even if the domain is the same, different ports, subdomains or protocols (https vs http) will also cause CORS errors.
|
||||
You will to need add a configuration on each server hosting these assets to allow your App server origin.
|
||||
|
||||
For example:
|
||||
|
||||
Lets assume your application is hosted on `https://my-ohif-server.com`.
|
||||
|
||||
Your JSON API is hosted on `https://my-json-api.aws.com`
|
||||
|
||||
And your images are stored on Amazon S3 at `https://my-s3-bucket.aws.com`
|
||||
|
||||
When you first start your application, browsing to `https://my-ohif-server.com/viewer?url=https://my-json-api.aws.com/api/my-json-study-info.json`, you will likely get a CORS error in the browser console as it tries to connect to `https://my-json-api.aws.com`.
|
||||
|
||||
Adding a setting on the JSON server to allow the CORS origin = `https://my-ohif-server.com` should solve this.
|
||||
|
||||
Next, you will likely get a similar CORS error, as the browser tries to go to `https://my-s3-bucket.aws.com`.
|
||||
You will need to go to the S3 bucket configuration, and add a CORS setting to allow origin = `https://my-ohif-server.com`.
|
||||
|
||||
Essentially, whenever the application connects to a remote resource, you will need to add the applications url to the allowed CORS Origins on that resource. Adding an origin similar to https://localhost:3000 will also allow for local testing.
|
||||
|
||||
...
|
||||
|
||||
### Securing Your Data
|
||||
|
||||
|
||||
@@ -8,7 +8,9 @@ OHIF Viewer and its medical imaging data. This post covers one of many potential
|
||||
setups that accomplish that. Please note, noticably absent is user account
|
||||
control.
|
||||
|
||||
Do not use this recipe to host sensitive medical data on the open web. Depending
|
||||
> **Do not use this recipe to host sensitive medical data on the open web!**
|
||||
|
||||
Depending
|
||||
on your company's policies, this may be an appropriate setup on an internal
|
||||
network when protected with a server's basic authentication. For a more robust
|
||||
setup, check out our [user account control recipe](./user-account-control.md)
|
||||
@@ -23,13 +25,13 @@ Our two biggest hurdles when hosting our image archive and web client are:
|
||||
|
||||
### Handling Web Requests
|
||||
|
||||
We mittigate our first issue by allowing [Nginx][nginx] to handle incoming web
|
||||
We mitigate our first issue by allowing [Nginx][nginx] to handle incoming web
|
||||
requests. Nginx is open source software for web serving, reverse proxying,
|
||||
caching, and more. It's designed for maximum performance and stability --
|
||||
allowing us to more reliably serve content than Orthanc's built-in server can.
|
||||
|
||||
More specifically, we accomplish this by using a
|
||||
[`reverse proxy`](https://en.wikipedia.org/wiki/Reverse_proxy) to retrieve
|
||||
[`reverse proxy`](https://en.wikipedia.org/wiki/Reverse_proxy) to retrieve
|
||||
resources from our image archive (Orthanc), and when accessing its web admin.
|
||||
|
||||
> A reverse proxy is a type of proxy server that retrieves resources on behalf
|
||||
@@ -38,7 +40,7 @@ resources from our image archive (Orthanc), and when accessing its web admin.
|
||||
|
||||
### CORS Issues
|
||||
|
||||
Cross-Origin Resource Sharing (CORS) is a mechanism that uses HTTP headers to
|
||||
[Cross-Origin Resource Sharing][understanding-cors] (CORS) is a mechanism that uses HTTP headers to
|
||||
tell a browser which web applications have permission to access selected
|
||||
resources from a server at a different origin (domain, protocol, port). IE. By
|
||||
default, a Web App located at `http://my-website.com` can't access resources
|
||||
@@ -51,9 +53,6 @@ We can solve this one of two ways:
|
||||
|
||||
**This solution uses the first approach.**
|
||||
|
||||
You can read more about CORS in this Medium article: [Understanding
|
||||
CORS][understanding-cors]
|
||||
|
||||
### Diagram
|
||||
|
||||
This setup allows us to create a setup similar to the one pictured below:
|
||||
@@ -73,6 +72,8 @@ This setup allows us to create a setup similar to the one pictured below:
|
||||
- Docker
|
||||
- [Docker for Mac](https://docs.docker.com/docker-for-mac/)
|
||||
- [Docker for Windows](https://docs.docker.com/docker-for-windows/)
|
||||
- Node 16.x with optional Python and Visual Studio Build Tools ([webpack 4 has an issue](https://github.com/webpack/webpack/issues/14532) with Node 17.x)
|
||||
- [Node.js](https://nodejs.org/en/download/)
|
||||
|
||||
_Not sure if you have `docker` installed already? Try running `docker --version`
|
||||
in command prompt or terminal_
|
||||
@@ -81,7 +82,7 @@ in command prompt or terminal_
|
||||
|
||||
_Spin Things Up_
|
||||
|
||||
- Navigate to `<project-root>/docker/OpenResty-Orthanc` in your shell
|
||||
- Navigate to `./platform/viewer/.recipes/OpenResty-Orthanc` in your shell
|
||||
- Run `docker-compose up`
|
||||
|
||||
_Upload Your First Study_
|
||||
@@ -94,16 +95,16 @@ _Upload Your First Study_
|
||||
|
||||
### Troubleshooting
|
||||
|
||||
_Exit code 137_
|
||||
#### Exit code 137
|
||||
|
||||
This means Docker ran out of memory. Open Docker Desktop, go to the `advanced`
|
||||
tab, and increase the amount of Memory available.
|
||||
|
||||
_Cannot create container for service X_
|
||||
#### Cannot create container for service X
|
||||
|
||||
Use this one with caution: `docker system prune`
|
||||
|
||||
_X is already running_
|
||||
#### X is already running
|
||||
|
||||
Stop running all containers:
|
||||
|
||||
@@ -120,15 +121,12 @@ likely want to update:
|
||||
#### OHIF Viewer
|
||||
|
||||
The OHIF Viewer's configuration is imported from a static `.js` file. The
|
||||
configuration we use is set to a specific file when we build the viewer, and
|
||||
configuration we use is set to [a specific file][config-ohif] when we build the viewer, and
|
||||
determined by the env variable: `APP_CONFIG`. You can see where we set its value
|
||||
in the `dockerfile` for this solution:
|
||||
|
||||
`ENV APP_CONFIG=config/docker_openresty-orthanc.js`
|
||||
|
||||
You can find the configuration we're using here:
|
||||
`/public/config/docker_openresty-orthanc.js`
|
||||
|
||||
To rebuild the `webapp` image created by our `dockerfile` after updating the
|
||||
Viewer's configuration, you can run:
|
||||
|
||||
@@ -137,7 +135,7 @@ Viewer's configuration, you can run:
|
||||
|
||||
#### Other
|
||||
|
||||
All other files are found in: `/docker/OpenResty-Orthanc/`
|
||||
All other files in this directory include
|
||||
|
||||
| Service | Configuration | Docs |
|
||||
| ----------------- | --------------------------------- | ------------------------------------------- |
|
||||
@@ -236,6 +234,8 @@ members put together:
|
||||
[orthanc-docs]: http://book.orthanc-server.com/users/configuration.html#configuration
|
||||
[lua-resty-openidc-docs]: https://github.com/zmartzone/lua-resty-openidc
|
||||
<!-- SRC -->
|
||||
|
||||
[config-ohif]: https://github.com/OHIF/Viewers/blob/master/platform/viewer/public/config/docker_openresty-orthanc.js
|
||||
[dockerfile]: https://github.com/OHIF/Viewers/blob/master/platform/viewer/.recipes/OpenResty-Orthanc/dockerfile
|
||||
[config-nginx]: https://github.com/OHIF/Viewers/blob/master/platform/viewer/.recipes/OpenResty-Orthanc/config/nginx.conf
|
||||
[config-orthanc]: https://github.com/OHIF/Viewers/blob/master/platform/viewer/.recipes/OpenResty-Orthanc/config/orthanc.json
|
||||
|
||||
@@ -40,5 +40,10 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"gitbook-cli": "^2.3.2"
|
||||
},
|
||||
"resolutions": {
|
||||
"underscore": "1.12.1",
|
||||
"json-schema": "0.4.0",
|
||||
"minimist": "1.2.8"
|
||||
}
|
||||
}
|
||||
@@ -67,6 +67,33 @@ window.config = {
|
||||
extensions: [],
|
||||
showStudyList: true,
|
||||
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: {
|
||||
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: [
|
||||
{ commandName: 'rotateViewportCW', label: 'Rotate Right', keys: ['r'] },
|
||||
{ commandName: 'rotateViewportCCW', label: 'Rotate Left', keys: ['l'] },
|
||||
@@ -97,14 +131,16 @@ window.config = {
|
||||
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.
|
||||
* Until we're able to decouple it, this key will serve as a workaround to
|
||||
* pass it configuration.
|
||||
*
|
||||
* @param {boolean} hideHandles Whether to show/hide annotation "handles"
|
||||
*/
|
||||
cornerstoneExtensionConfig: {
|
||||
/* Whether to show/hide annotation "handles" */
|
||||
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,
|
||||
},
|
||||
};
|
||||
```
|
||||
+31
-34
@@ -1481,10 +1481,10 @@ json-schema-traverse@^0.4.1:
|
||||
resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660"
|
||||
integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==
|
||||
|
||||
json-schema@0.2.3:
|
||||
version "0.2.3"
|
||||
resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13"
|
||||
integrity sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=
|
||||
json-schema@0.2.3, json-schema@0.4.0:
|
||||
version "0.4.0"
|
||||
resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.4.0.tgz#f7de4cf6efab838ebaeb3236474cbba5a1930ab5"
|
||||
integrity sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==
|
||||
|
||||
json-stable-stringify@^1.0.1:
|
||||
version "1.0.1"
|
||||
@@ -1729,27 +1729,24 @@ mime-types@^2.1.11, mime-types@^2.1.12, mime-types@~2.1.19, mime-types@~2.1.7:
|
||||
dependencies:
|
||||
mime-db "~1.38.0"
|
||||
|
||||
minimatch@^3.0.0, minimatch@^3.0.2, minimatch@^3.0.4, minimatch@~3.0.3:
|
||||
version "3.0.4"
|
||||
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083"
|
||||
integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==
|
||||
minimatch@^3.0.0, minimatch@^3.0.2, minimatch@^3.0.4:
|
||||
version "3.1.2"
|
||||
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b"
|
||||
integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==
|
||||
dependencies:
|
||||
brace-expansion "^1.1.7"
|
||||
|
||||
minimist@0.0.8:
|
||||
version "0.0.8"
|
||||
resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d"
|
||||
integrity sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=
|
||||
minimatch@~3.0.3:
|
||||
version "3.0.8"
|
||||
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.8.tgz#5e6a59bd11e2ab0de1cfb843eb2d82e546c321c1"
|
||||
integrity sha512-6FsRAQsxQ61mw+qP1ZzbL9Bc78x2p5OqNgNpnoAFLTrX8n5Kxph0CsnhmKKNXTWjXqU5L0pGPR7hYk+XWZr60Q==
|
||||
dependencies:
|
||||
brace-expansion "^1.1.7"
|
||||
|
||||
minimist@^1.2.0:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284"
|
||||
integrity sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=
|
||||
|
||||
minimist@~0.0.1:
|
||||
version "0.0.10"
|
||||
resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.10.tgz#de3f98543dbf96082be48ad1a0c7cda836301dcf"
|
||||
integrity sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8=
|
||||
minimist@0.0.8, minimist@1.2.8, minimist@^1.2.0, minimist@~0.0.1:
|
||||
version "1.2.8"
|
||||
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c"
|
||||
integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==
|
||||
|
||||
mississippi@^1.2.0, mississippi@^1.3.0, mississippi@~1.3.0:
|
||||
version "1.3.1"
|
||||
@@ -2491,19 +2488,19 @@ q@1.5.0:
|
||||
integrity sha1-3QG6ydBtMObyGa7LglPunr3DCPE=
|
||||
|
||||
qs@~6.2.0:
|
||||
version "6.2.3"
|
||||
resolved "https://registry.yarnpkg.com/qs/-/qs-6.2.3.tgz#1cfcb25c10a9b2b483053ff39f5dfc9233908cfe"
|
||||
integrity sha1-HPyyXBCpsrSDBT/zn138kjOQjP4=
|
||||
version "6.2.4"
|
||||
resolved "https://registry.yarnpkg.com/qs/-/qs-6.2.4.tgz#d90821bb8537cecc140e6c34f54ec76e54b39b22"
|
||||
integrity sha512-E57gmgKXqDda+qWTkUJgIwgJICK7zgMfqZZopTRKZ6mY9gzLlmJN9EpXNnDrTxXFlOM/a+I28kJkF/60rqgnYw==
|
||||
|
||||
qs@~6.4.0:
|
||||
version "6.4.0"
|
||||
resolved "https://registry.yarnpkg.com/qs/-/qs-6.4.0.tgz#13e26d28ad6b0ffaa91312cd3bf708ed351e7233"
|
||||
integrity sha1-E+JtKK1rD/qpExLNO/cI7TUecjM=
|
||||
version "6.4.1"
|
||||
resolved "https://registry.yarnpkg.com/qs/-/qs-6.4.1.tgz#2bad97710a5b661c366b378b1e3a44a592ff45e6"
|
||||
integrity sha512-LQy1Q1fcva/UsnP/6Iaa4lVeM49WiOitu2T4hZCyA/elLKu37L99qcBJk4VCCk+rdLvnMzfKyiN3SZTqdAZGSQ==
|
||||
|
||||
qs@~6.5.2:
|
||||
version "6.5.2"
|
||||
resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36"
|
||||
integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==
|
||||
version "6.5.3"
|
||||
resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.3.tgz#3aeeffc91967ef6e35c0e488ef46fb296ab76aad"
|
||||
integrity sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==
|
||||
|
||||
rc@^1.0.1, rc@^1.1.6:
|
||||
version "1.2.8"
|
||||
@@ -3175,10 +3172,10 @@ umask@~1.1.0:
|
||||
resolved "https://registry.yarnpkg.com/umask/-/umask-1.1.0.tgz#f29cebf01df517912bb58ff9c4e50fde8e33320d"
|
||||
integrity sha1-8pzr8B31F5ErtY/5xOUP3o4zMg0=
|
||||
|
||||
underscore@^1.7.0:
|
||||
version "1.9.1"
|
||||
resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.9.1.tgz#06dce34a0e68a7babc29b365b8e74b8925203961"
|
||||
integrity sha512-5/4etnCkd9c8gwgowi5/om/mYO5ajCaOgdzj/oW+0eQV9WxKBDZw5+ycmKmeaTXjInS/W0BzpGLo2xR2aBwZdg==
|
||||
underscore@1.12.1, underscore@^1.7.0:
|
||||
version "1.12.1"
|
||||
resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.12.1.tgz#7bb8cc9b3d397e201cf8553336d262544ead829e"
|
||||
integrity sha512-hEQt0+ZLDVUMhebKxL4x1BTtDY7bavVofhZ9KZ4aI26X9SRaE+Y3m83XUL1UP2jn8ynjndwCCpEHdUG+9pP1Tw==
|
||||
|
||||
unique-filename@^1.1.0, unique-filename@~1.1.0:
|
||||
version "1.1.1"
|
||||
|
||||
+2
-2
@@ -8,7 +8,6 @@
|
||||
"-fontsettings",
|
||||
"github",
|
||||
"ga",
|
||||
"sitemap",
|
||||
"anchors",
|
||||
"versions"
|
||||
],
|
||||
@@ -24,7 +23,8 @@
|
||||
"token": "UA-110573590-2"
|
||||
},
|
||||
"sitemap": {
|
||||
"hostname": "https://docs.ohif.org"
|
||||
"hostname": "https://docs.ohif.org",
|
||||
"todo": "sitemap plugin removed; causing build errors"
|
||||
},
|
||||
"versions": {
|
||||
"gitbookConfigURL": "https://raw.githubusercontent.com/OHIF/Viewers/master/docs/book.json",
|
||||
|
||||
@@ -3,6 +3,408 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [2.12.16](https://github.com/OHIF/Viewers/compare/@ohif/extension-cornerstone@2.12.15...@ohif/extension-cornerstone@2.12.16) (2022-12-21)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [2.12.15](https://github.com/OHIF/Viewers/compare/@ohif/extension-cornerstone@2.12.14...@ohif/extension-cornerstone@2.12.15) (2022-09-20)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [2.12.14](https://github.com/OHIF/Viewers/compare/@ohif/extension-cornerstone@2.12.13...@ohif/extension-cornerstone@2.12.14) (2022-09-01)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [2.12.13](https://github.com/OHIF/Viewers/compare/@ohif/extension-cornerstone@2.12.12...@ohif/extension-cornerstone@2.12.13) (2022-06-30)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [2.12.12](https://github.com/OHIF/Viewers/compare/@ohif/extension-cornerstone@2.12.11...@ohif/extension-cornerstone@2.12.12) (2022-06-29)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [2.12.11](https://github.com/OHIF/Viewers/compare/@ohif/extension-cornerstone@2.12.10...@ohif/extension-cornerstone@2.12.11) (2022-06-14)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [2.12.10](https://github.com/OHIF/Viewers/compare/@ohif/extension-cornerstone@2.12.9...@ohif/extension-cornerstone@2.12.10) (2022-04-05)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [2.12.9](https://github.com/OHIF/Viewers/compare/@ohif/extension-cornerstone@2.12.8...@ohif/extension-cornerstone@2.12.9) (2022-03-25)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [2.12.8](https://github.com/OHIF/Viewers/compare/@ohif/extension-cornerstone@2.12.7...@ohif/extension-cornerstone@2.12.8) (2022-03-14)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [2.12.7](https://github.com/OHIF/Viewers/compare/@ohif/extension-cornerstone@2.12.6...@ohif/extension-cornerstone@2.12.7) (2022-02-03)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [2.12.6](https://github.com/OHIF/Viewers/compare/@ohif/extension-cornerstone@2.12.5...@ohif/extension-cornerstone@2.12.6) (2022-02-03)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [2.12.5](https://github.com/OHIF/Viewers/compare/@ohif/extension-cornerstone@2.12.4...@ohif/extension-cornerstone@2.12.5) (2022-02-02)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [2.12.4](https://github.com/OHIF/Viewers/compare/@ohif/extension-cornerstone@2.12.3...@ohif/extension-cornerstone@2.12.4) (2022-01-20)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [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)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [2.9.5](https://github.com/OHIF/Viewers/compare/@ohif/extension-cornerstone@2.9.4...@ohif/extension-cornerstone@2.9.5) (2020-09-29)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [2.9.4](https://github.com/OHIF/Viewers/compare/@ohif/extension-cornerstone@2.9.3...@ohif/extension-cornerstone@2.9.4) (2020-09-03)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [2.9.3](https://github.com/OHIF/Viewers/compare/@ohif/extension-cornerstone@2.9.2...@ohif/extension-cornerstone@2.9.3) (2020-09-02)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [2.9.2](https://github.com/OHIF/Viewers/compare/@ohif/extension-cornerstone@2.9.1...@ohif/extension-cornerstone@2.9.2) (2020-08-20)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Updated react-cornerstone-viewport to version 4.0.2 ([#2001](https://github.com/OHIF/Viewers/issues/2001)) ([59ab68b](https://github.com/OHIF/Viewers/commit/59ab68b4edbfec010170c4429c300a53f688bf94))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [2.9.1](https://github.com/OHIF/Viewers/compare/@ohif/extension-cornerstone@2.9.0...@ohif/extension-cornerstone@2.9.1) (2020-08-20)
|
||||
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* **stackPrefetch:** Added stackPrefetch config with 20 max concurrent requests ([#2000](https://github.com/OHIF/Viewers/issues/2000)) ([3b02a06](https://github.com/OHIF/Viewers/commit/3b02a06f5e250660edde4862ce44147db3dc3ab9))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [2.9.0](https://github.com/OHIF/Viewers/compare/@ohif/extension-cornerstone@2.8.5...@ohif/extension-cornerstone@2.9.0) (2020-08-18)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* 🎸 Update react-vtkjs-viewport usage to use requestPool ([#1984](https://github.com/OHIF/Viewers/issues/1984)) ([bb5f30c](https://github.com/OHIF/Viewers/commit/bb5f30ce2a0192d2e021beaaadfff22fd38e17b9))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [2.8.5](https://github.com/OHIF/Viewers/compare/@ohif/extension-cornerstone@2.8.4...@ohif/extension-cornerstone@2.8.5) (2020-08-05)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [2.8.4](https://github.com/OHIF/Viewers/compare/@ohif/extension-cornerstone@2.8.3...@ohif/extension-cornerstone@2.8.4) (2020-07-13)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* 🐛 - Put guards in all places that a cornerstone re-render ([#1899](https://github.com/OHIF/Viewers/issues/1899)) ([451f7ea](https://github.com/OHIF/Viewers/commit/451f7eab9258e7a193eb362e0926b13aedc4b3c9))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [2.8.3](https://github.com/OHIF/Viewers/compare/@ohif/extension-cornerstone@2.8.2...@ohif/extension-cornerstone@2.8.3) (2020-07-13)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [2.8.2](https://github.com/OHIF/Viewers/compare/@ohif/extension-cornerstone@2.8.1...@ohif/extension-cornerstone@2.8.2) (2020-06-15)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* 🐛 Disable seg panel when data for seg unavailable ([#1732](https://github.com/OHIF/Viewers/issues/1732)) ([698e900](https://github.com/OHIF/Viewers/commit/698e900b85121d3c2a46747c443ef69fb7a8c95b)), closes [#1728](https://github.com/OHIF/Viewers/issues/1728)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [2.8.1](https://github.com/OHIF/Viewers/compare/@ohif/extension-cornerstone@2.8.0...@ohif/extension-cornerstone@2.8.1) (2020-06-04)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* 🐛 Infinite frame index change when quickly jumping between ([#1734](https://github.com/OHIF/Viewers/issues/1734)) ([a830577](https://github.com/OHIF/Viewers/commit/a8305772d0fbec506549ad7ea08504a8bc3a4f39)), closes [#1733](https://github.com/OHIF/Viewers/issues/1733)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [2.8.0](https://github.com/OHIF/Viewers/compare/@ohif/extension-cornerstone@2.7.3...@ohif/extension-cornerstone@2.8.0) (2020-06-04)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* 🎸 1729 - error boundary wrapper ([#1764](https://github.com/OHIF/Viewers/issues/1764)) ([c02b232](https://github.com/OHIF/Viewers/commit/c02b232b0cc24f38af5d5e3831d987d048e60ada))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [2.7.3](https://github.com/OHIF/Viewers/compare/@ohif/extension-cornerstone@2.7.2...@ohif/extension-cornerstone@2.7.3) (2020-05-12)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* 🐛 Fix seg color load ([#1724](https://github.com/OHIF/Viewers/issues/1724)) ([c4f84b1](https://github.com/OHIF/Viewers/commit/c4f84b1174d04ba84d37ed89b6d7ab541be28181))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [2.7.2](https://github.com/OHIF/Viewers/compare/@ohif/extension-cornerstone@2.7.1...@ohif/extension-cornerstone@2.7.2) (2020-05-04)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* 🐛 Proper error handling for derived display sets ([#1708](https://github.com/OHIF/Viewers/issues/1708)) ([5b20d8f](https://github.com/OHIF/Viewers/commit/5b20d8f323e4b3ef9988f2f2ab672d697b6da409))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [2.7.1](https://github.com/OHIF/Viewers/compare/@ohif/extension-cornerstone@2.7.0...@ohif/extension-cornerstone@2.7.1) (2020-05-04)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [2.7.0](https://github.com/OHIF/Viewers/compare/@ohif/extension-cornerstone@2.6.1...@ohif/extension-cornerstone@2.7.0) (2020-04-24)
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
*We currently support one property for annotation tools.*
|
||||
_We currently support one property for annotation tools._
|
||||
|
||||
### 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
|
||||
...
|
||||
@@ -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]: https://github.com/cornerstonejs/cornerstone
|
||||
<!-- prettier-ignore-end -->
|
||||
```
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@ohif/extension-cornerstone",
|
||||
"version": "2.7.0",
|
||||
"version": "2.12.16",
|
||||
"description": "OHIF extension for Cornerstone",
|
||||
"author": "OHIF",
|
||||
"license": "MIT",
|
||||
@@ -32,25 +32,26 @@
|
||||
"peerDependencies": {
|
||||
"@ohif/core": "^0.50.0",
|
||||
"@ohif/ui": "^0.50.0",
|
||||
"cornerstone-core": "^2.2.8",
|
||||
"cornerstone-math": "^0.1.8",
|
||||
"cornerstone-tools": "4.12.5",
|
||||
"cornerstone-wado-image-loader": "^3.1.0",
|
||||
"dcmjs": "^0.12.2",
|
||||
"dicom-parser": "^1.8.3",
|
||||
"cornerstone-core": "^2.6.1",
|
||||
"cornerstone-math": "^0.1.9",
|
||||
"cornerstone-tools": "^6.0.6",
|
||||
"cornerstone-wado-image-loader": "^4.2.1",
|
||||
"dcmjs": "0.29.3",
|
||||
"dicom-parser": "^1.8.11",
|
||||
"hammerjs": "^2.0.8",
|
||||
"prop-types": "^15.6.2",
|
||||
"react": "^16.8.6",
|
||||
"react-dom": "^16.8.6",
|
||||
"react-redux": "^6.0.0",
|
||||
"react-resize-detector": "^3.4.0",
|
||||
"redux": "^4.0.1"
|
||||
"redux": "^4.0.1",
|
||||
"styled-components": "^5.3.5"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.5.5",
|
||||
"classnames": "^2.2.6",
|
||||
"lodash.merge": "^4.6.2",
|
||||
"lodash.throttle": "^4.1.1",
|
||||
"react-cornerstone-viewport": "^2.3.6"
|
||||
"react-cornerstone-viewport": "4.1.1"
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@ import OHIF from '@ohif/core';
|
||||
import { connect } from 'react-redux';
|
||||
import throttle from 'lodash.throttle';
|
||||
import { setEnabledElement } from './state';
|
||||
import initSRTools from './tools/initSRTools';
|
||||
|
||||
const { setViewportActive, setViewportSpecificData } = OHIF.redux.actions;
|
||||
const {
|
||||
@@ -53,7 +54,11 @@ const mapStateToProps = (state, ownProps) => {
|
||||
// Currently justing using escape hatch + commands
|
||||
// activeTool: activeButton && activeButton.command,
|
||||
...dataFromStore,
|
||||
isStackPrefetchEnabled: isActive,
|
||||
isStackPrefetchEnabled: ownProps.hasOwnProperty('isStackPrefetchEnabled')
|
||||
? ownProps.isStackPrefetchEnabled
|
||||
: ownProps.stackPrefetch
|
||||
? ownProps.stackPrefetch.enabled
|
||||
: isActive,
|
||||
isPlaying,
|
||||
frameRate,
|
||||
//stack: viewportSpecificData.stack,
|
||||
@@ -88,6 +93,7 @@ const mapDispatchToProps = (dispatch, ownProps) => {
|
||||
plugin: 'cornerstone',
|
||||
})
|
||||
);
|
||||
initSRTools(enabledElement);
|
||||
},
|
||||
|
||||
onMeasurementsChanged: (event, action) => {
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import React, { Component } from 'react';
|
||||
|
||||
import OHIFCornerstoneViewportOverlay from './components/OHIFCornerstoneViewportOverlay';
|
||||
import ConnectedCornerstoneViewport from './ConnectedCornerstoneViewport';
|
||||
import OHIF from '@ohif/core';
|
||||
import PropTypes from 'prop-types';
|
||||
import cornerstone from 'cornerstone-core';
|
||||
import checkForSRAnnotations from './tools/checkForSRAnnotations';
|
||||
|
||||
const { StackManager } = OHIF.utils;
|
||||
|
||||
@@ -14,6 +16,7 @@ class OHIFCornerstoneViewport extends Component {
|
||||
|
||||
static defaultProps = {
|
||||
customProps: {},
|
||||
isStackPrefetchEnabled: true,
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
@@ -22,6 +25,8 @@ class OHIFCornerstoneViewport extends Component {
|
||||
viewportIndex: PropTypes.number,
|
||||
children: PropTypes.node,
|
||||
customProps: PropTypes.object,
|
||||
stackPrefetch: PropTypes.object,
|
||||
isStackPrefetchEnabled: PropTypes.bool,
|
||||
};
|
||||
|
||||
static id = 'OHIFCornerstoneViewport';
|
||||
@@ -183,6 +188,8 @@ class OHIFCornerstoneViewport extends Component {
|
||||
displaySet.SOPInstanceUID !== prevDisplaySet.SOPInstanceUID ||
|
||||
displaySet.frameIndex !== prevDisplaySet.frameIndex
|
||||
) {
|
||||
const { viewportIndex } = this.props;
|
||||
checkForSRAnnotations({ displaySet, viewportIndex });
|
||||
this.setStateFromProps();
|
||||
}
|
||||
}
|
||||
@@ -194,6 +201,7 @@ class OHIFCornerstoneViewport extends Component {
|
||||
return null;
|
||||
}
|
||||
const { viewportIndex } = this.props;
|
||||
const { inconsistencyWarnings } = this.props.viewportData.displaySet;
|
||||
const {
|
||||
imageIds,
|
||||
currentImageIdIndex,
|
||||
@@ -215,16 +223,53 @@ class OHIFCornerstoneViewport extends Component {
|
||||
});
|
||||
}
|
||||
|
||||
const newImageHandler = ({ currentImageIdIndex, sopInstanceUid }) => {
|
||||
const { displaySet } = this.props.viewportData;
|
||||
const { StudyInstanceUID } = displaySet;
|
||||
|
||||
if (currentImageIdIndex >= 0) {
|
||||
this.props.onNewImage({
|
||||
StudyInstanceUID,
|
||||
SOPInstanceUID: sopInstanceUid,
|
||||
frameIndex: currentImageIdIndex,
|
||||
activeViewportIndex: viewportIndex,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const overlay = props => {
|
||||
const displaySet = this.props.viewportData.displaySet;
|
||||
let filteredSRLabels;
|
||||
if (displaySet.SRLabels && displaySet.SRLabels.length !== 0) {
|
||||
filteredSRLabels = displaySet.SRLabels.filter(
|
||||
SRLabel =>
|
||||
SRLabel.ReferencedSOPInstanceUID === displaySet.SOPInstanceUID
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<OHIFCornerstoneViewportOverlay
|
||||
{...props}
|
||||
inconsistencyWarnings={inconsistencyWarnings}
|
||||
SRLabels={filteredSRLabels}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<ConnectedCornerstoneViewport
|
||||
viewportIndex={viewportIndex}
|
||||
imageIds={imageIds}
|
||||
imageIdIndex={currentImageIdIndex}
|
||||
onNewImageDebounced={newImageHandler}
|
||||
onNewImageDebounceTime={300}
|
||||
viewportOverlayComponent={overlay}
|
||||
stackPrefetch={this.props.stackPrefetch}
|
||||
isStackPrefetchEnabled={this.props.isStackPrefetchEnabled}
|
||||
// ~~ Connected (From REDUX)
|
||||
// frameRate={frameRate}
|
||||
// isPlaying={false}
|
||||
// isStackPrefetchEnabled={true}
|
||||
// onElementEnabled={() => {}}
|
||||
// setViewportActive{() => {}}
|
||||
{...this.props.customProps}
|
||||
|
||||
@@ -10,6 +10,14 @@ const scroll = cornerstoneTools.import('util/scroll');
|
||||
const { studyMetadataManager } = OHIF.utils;
|
||||
const { setViewportSpecificData } = OHIF.redux.actions;
|
||||
|
||||
const refreshCornerstoneViewports = () => {
|
||||
cornerstone.getEnabledElements().forEach(enabledElement => {
|
||||
if (enabledElement.image) {
|
||||
cornerstone.updateImage(enabledElement.element);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const commandsModule = ({ servicesManager }) => {
|
||||
const actions = {
|
||||
rotateViewport: ({ viewports, rotation }) => {
|
||||
@@ -180,10 +188,7 @@ const commandsModule = ({ servicesManager }) => {
|
||||
|
||||
measurementApi.syncMeasurementsAndToolData();
|
||||
|
||||
// Update images in all active viewports
|
||||
cornerstone.getEnabledElements().forEach(enabledElement => {
|
||||
cornerstone.updateImage(enabledElement.element);
|
||||
});
|
||||
refreshCornerstoneViewports();
|
||||
},
|
||||
getNearbyToolData({ element, canvasCoordinates, availableToolTypes }) {
|
||||
const nearbyTool = {};
|
||||
@@ -262,23 +267,33 @@ const commandsModule = ({ servicesManager }) => {
|
||||
StudyInstanceUID,
|
||||
SOPInstanceUID,
|
||||
frameIndex,
|
||||
activeViewportIndex
|
||||
activeViewportIndex,
|
||||
refreshViewports = true,
|
||||
}) => {
|
||||
const study = studyMetadataManager.get(StudyInstanceUID);
|
||||
|
||||
const displaySet = study.findDisplaySet(ds => {
|
||||
return ds.images && ds.images.find(i => i.getSOPInstanceUID() === SOPInstanceUID)
|
||||
return (
|
||||
ds.images &&
|
||||
ds.images.find(i => i.getSOPInstanceUID() === SOPInstanceUID)
|
||||
);
|
||||
});
|
||||
|
||||
if (!displaySet) {
|
||||
return;
|
||||
}
|
||||
|
||||
displaySet.SOPInstanceUID = SOPInstanceUID;
|
||||
displaySet.frameIndex = frameIndex;
|
||||
|
||||
window.store.dispatch(setViewportSpecificData(activeViewportIndex, displaySet));
|
||||
window.store.dispatch(
|
||||
setViewportSpecificData(activeViewportIndex, displaySet)
|
||||
);
|
||||
|
||||
cornerstone.getEnabledElements().forEach(enabledElement => {
|
||||
cornerstone.updateImage(enabledElement.element);
|
||||
});
|
||||
}
|
||||
if (refreshViewports) {
|
||||
refreshCornerstoneViewports();
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
const definitions = {
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
.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-left3 {
|
||||
bottom: 110px;
|
||||
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,267 @@
|
||||
import { PureComponent } from 'react';
|
||||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
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';
|
||||
|
||||
const Button = styled.button`
|
||||
color: white;
|
||||
padding: 2px 7px;
|
||||
border-radius: 10px;
|
||||
outline: 0;
|
||||
text-transform: none;
|
||||
margin: 2px 2px;
|
||||
cursor: pointer;
|
||||
&:disabled {
|
||||
cursor: default;
|
||||
opacity: 0.9;
|
||||
}
|
||||
`;
|
||||
|
||||
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,
|
||||
SRLabels: PropTypes.array,
|
||||
};
|
||||
|
||||
render() {
|
||||
const {
|
||||
imageId,
|
||||
scale,
|
||||
windowWidth,
|
||||
windowCenter,
|
||||
inconsistencyWarnings,
|
||||
SRLabels,
|
||||
} = 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 SRLabelsOn = SRLabels && SRLabels.length !== 0 ? true : false;
|
||||
|
||||
/**/
|
||||
|
||||
const getSRLabelsContent = SRLabels => {
|
||||
if (Array.isArray(SRLabels)) {
|
||||
const listedSRLabels = SRLabels.map((SRLabel, index) => {
|
||||
const color = SRLabel.labels.color;
|
||||
return (
|
||||
SRLabel.labels.visible && (
|
||||
<OverlayTrigger
|
||||
key={index}
|
||||
placement="top"
|
||||
overlay={
|
||||
<Tooltip
|
||||
placement="top"
|
||||
className="in tooltip-warning"
|
||||
id="tooltip-top"
|
||||
>
|
||||
<div className="warningTitle">
|
||||
{' '}
|
||||
Coding scheme designators{' '}
|
||||
</div>
|
||||
<div className="warningContent">
|
||||
{SRLabel.labels.labelCodingSchemeDesignator +
|
||||
' : ' +
|
||||
SRLabel.labels.valueCodingSchemeDesignator}
|
||||
</div>
|
||||
</Tooltip>
|
||||
}
|
||||
>
|
||||
<div style={{ display: 'inline-block' }}>
|
||||
<Button
|
||||
style={{
|
||||
backgroundColor: color,
|
||||
}}
|
||||
disabled={true}
|
||||
key={index}
|
||||
>
|
||||
{SRLabel.labels.label + ' : ' + SRLabel.labels.value}
|
||||
</Button>
|
||||
</div>
|
||||
</OverlayTrigger>
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
return <ol>{listedSRLabels}</ol>;
|
||||
} else {
|
||||
return <React.Fragment></React.Fragment>;
|
||||
}
|
||||
};
|
||||
|
||||
const getSRLabelsInfo = SRLabels => {
|
||||
return (
|
||||
<React.Fragment>
|
||||
{SRLabels.length != 0 ? (
|
||||
getSRLabelsContent(SRLabels)
|
||||
) : (
|
||||
<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-left3 warning">
|
||||
<div>{SRLabelsOn ? getSRLabelsInfo(SRLabels) : ''}</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;
|
||||
@@ -3,6 +3,7 @@ import init from './init.js';
|
||||
import commandsModule from './commandsModule.js';
|
||||
import toolbarModule from './toolbarModule.js';
|
||||
import CornerstoneViewportDownloadForm from './CornerstoneViewportDownloadForm';
|
||||
import { version } from '../package.json';
|
||||
|
||||
const Component = React.lazy(() => {
|
||||
return import('./OHIFCornerstoneViewport');
|
||||
@@ -24,6 +25,7 @@ export default {
|
||||
* Only required property. Should be a unique value across all extensions.
|
||||
*/
|
||||
id: 'cornerstone',
|
||||
version,
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -34,8 +36,33 @@ export default {
|
||||
preRegistration({ servicesManager, configuration = {} }) {
|
||||
init({ servicesManager, configuration });
|
||||
},
|
||||
getViewportModule() {
|
||||
return OHIFCornerstoneViewport;
|
||||
getViewportModule({ commandsManager, appConfig }) {
|
||||
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 => {
|
||||
/** Do not trigger all viewports to render unnecessarily */
|
||||
jumpData.refreshViewports = false;
|
||||
commandsManager.runCommand('jumpToImage', jumpData);
|
||||
};
|
||||
|
||||
const { studyPrefetcher } = appConfig;
|
||||
const isStackPrefetchEnabled =
|
||||
studyPrefetcher && !studyPrefetcher.enabled;
|
||||
|
||||
return (
|
||||
<OHIFCornerstoneViewport
|
||||
{...props}
|
||||
onNewImage={onNewImageHandler}
|
||||
isStackPrefetchEnabled={isStackPrefetchEnabled}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
return ExtendedOHIFCornerstoneViewport;
|
||||
},
|
||||
getToolbarModule() {
|
||||
return toolbarModule;
|
||||
|
||||
@@ -5,6 +5,8 @@ import csTools from 'cornerstone-tools';
|
||||
import merge from 'lodash.merge';
|
||||
import initCornerstoneTools from './initCornerstoneTools.js';
|
||||
import measurementServiceMappingsFactory from './utils/measurementServiceMappings/measurementServiceMappingsFactory';
|
||||
import dicomSRModule from './tools/modules/dicomSRModule';
|
||||
import srModuleId from './tools/id';
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -15,6 +17,8 @@ import measurementServiceMappingsFactory from './utils/measurementServiceMapping
|
||||
export default function init({ servicesManager, configuration }) {
|
||||
const { UIDialogService, MeasurementService } = servicesManager.services;
|
||||
|
||||
csTools.register('module', srModuleId, dicomSRModule);
|
||||
|
||||
const callInputDialog = (data, event, callback) => {
|
||||
if (UIDialogService) {
|
||||
let dialogId = UIDialogService.create({
|
||||
@@ -37,7 +41,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;
|
||||
|
||||
cornerstone.metaData.addProvider(
|
||||
@@ -52,7 +63,7 @@ export default function init({ servicesManager, configuration }) {
|
||||
autoResizeViewports: false,
|
||||
};
|
||||
|
||||
initCornerstoneTools(defaultCsToolsConfig);
|
||||
initCornerstoneTools({ ...defaultCsToolsConfig, ...stackPrefetch });
|
||||
|
||||
const toolsGroupedByType = {
|
||||
touch: [csTools.PanMultiTouchTool, csTools.ZoomTouchPinchTool],
|
||||
|
||||
@@ -34,4 +34,11 @@ export default function(configuration = {}) {
|
||||
cornerstoneTools.toolColors.setActiveColor('rgb(0, 255, 0)');
|
||||
|
||||
cornerstoneTools.store.state.touchProximity = 40;
|
||||
|
||||
// Configure stack prefetch
|
||||
cornerstoneTools.stackPrefetch.setConfiguration({
|
||||
maxImagesToPrefetch: configuration.maxImagesToPrefetch,
|
||||
preserveExistingPool: configuration.preserveExistingPool,
|
||||
maxSimultaneousRequests: configuration.maxSimultaneousRequests,
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,419 @@
|
||||
import csTools, {
|
||||
importInternal,
|
||||
getToolState,
|
||||
toolColors,
|
||||
} from 'cornerstone-tools';
|
||||
|
||||
import cornerstone from 'cornerstone-core';
|
||||
|
||||
/** Internal imports */
|
||||
import TOOL_NAMES from './constants/toolNames';
|
||||
import SCOORD_TYPES from './constants/scoordTypes';
|
||||
import id from './id';
|
||||
|
||||
/** Cornerstone 3rd party dev kit imports */
|
||||
const draw = importInternal('drawing/draw');
|
||||
const drawJoinedLines = importInternal('drawing/drawJoinedLines');
|
||||
const drawCircle = importInternal('drawing/drawCircle');
|
||||
const drawEllipse = importInternal('drawing/drawEllipse');
|
||||
const drawHandles = importInternal('drawing/drawHandles');
|
||||
const drawArrow = importInternal('drawing/drawArrow');
|
||||
const getNewContext = importInternal('drawing/getNewContext');
|
||||
const BaseTool = importInternal('base/BaseTool');
|
||||
const drawLinkedTextBox = importInternal('drawing/drawLinkedTextBox');
|
||||
|
||||
/**
|
||||
* @class DICOMSRDisplayTool - Renders DICOMSR data in a read only manner (i.e. as an overlay).
|
||||
*
|
||||
* This is a generic render tool.
|
||||
*
|
||||
* A single tool that, given some schema, can render
|
||||
* POINT, MULTIPOINT, POLYLINE, CIRCLE, and ELLIPSE
|
||||
* value types for a given imageId.
|
||||
*
|
||||
*
|
||||
* @extends cornerstoneTools.BaseTool
|
||||
*/
|
||||
export default class DICOMSRDisplayTool extends BaseTool {
|
||||
constructor(props = {}) {
|
||||
const defaultProps = {
|
||||
mixins: ['enabledOrDisabledBinaryTool'],
|
||||
name: TOOL_NAMES.DICOM_SR_DISPLAY_TOOL,
|
||||
};
|
||||
|
||||
const initialProps = Object.assign(defaultProps, props);
|
||||
|
||||
super(initialProps);
|
||||
|
||||
this._module = csTools.getModule(id);
|
||||
}
|
||||
|
||||
renderToolData(evt) {
|
||||
const eventData = evt.detail;
|
||||
const { element } = eventData;
|
||||
const module = this._module;
|
||||
|
||||
const toolState = getToolState(element, this.name);
|
||||
|
||||
if (!toolState) {
|
||||
return;
|
||||
}
|
||||
|
||||
const trackingUniqueIdentifiersForElement = module.getters.trackingUniqueIdentifiersForElement(
|
||||
element
|
||||
);
|
||||
|
||||
const {
|
||||
activeIndex,
|
||||
trackingUniqueIdentifiers,
|
||||
} = trackingUniqueIdentifiersForElement;
|
||||
|
||||
const activeTrackingUniqueIdentifier =
|
||||
trackingUniqueIdentifiers[activeIndex];
|
||||
|
||||
// Filter toolData to only render the data for the active SR.
|
||||
const filteredToolData = toolState.data.filter(td =>
|
||||
trackingUniqueIdentifiers.includes(td.TrackingUniqueIdentifier)
|
||||
);
|
||||
|
||||
let shouldRepositionTextBoxes = false;
|
||||
|
||||
for (let i = 0; i < filteredToolData.length; i++) {
|
||||
const data = filteredToolData[i];
|
||||
const { renderableData, labels } = data;
|
||||
|
||||
const color =
|
||||
data.TrackingUniqueIdentifier === activeTrackingUniqueIdentifier
|
||||
? toolColors.getActiveColor()
|
||||
: toolColors.getToolColor();
|
||||
const lineWidth = 2;
|
||||
const options = {
|
||||
color,
|
||||
lineWidth,
|
||||
handleRadius: 6,
|
||||
};
|
||||
|
||||
Object.keys(renderableData).forEach(GraphicType => {
|
||||
const renderableDataForGraphicType = renderableData[GraphicType];
|
||||
|
||||
switch (GraphicType) {
|
||||
case SCOORD_TYPES.TEXT:
|
||||
break;
|
||||
case SCOORD_TYPES.POINT:
|
||||
this.renderPoint(renderableDataForGraphicType, eventData, options);
|
||||
break;
|
||||
case SCOORD_TYPES.MULTIPOINT:
|
||||
this.renderMultipoint(
|
||||
renderableDataForGraphicType,
|
||||
eventData,
|
||||
options
|
||||
);
|
||||
break;
|
||||
case SCOORD_TYPES.POLYGON:
|
||||
case SCOORD_TYPES.POLYLINE:
|
||||
this.renderPolyLine(
|
||||
renderableDataForGraphicType,
|
||||
eventData,
|
||||
options
|
||||
);
|
||||
break;
|
||||
case SCOORD_TYPES.CIRCLE:
|
||||
this.renderCircle(renderableDataForGraphicType, eventData, options);
|
||||
break;
|
||||
case SCOORD_TYPES.ELLIPSE:
|
||||
this.renderEllipse(
|
||||
renderableDataForGraphicType,
|
||||
eventData,
|
||||
options
|
||||
);
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
const { element } = eventData;
|
||||
const context = getNewContext(eventData.canvasContext.canvas);
|
||||
|
||||
if (!data.handles || !data.handles.textBox) {
|
||||
const textBox = {
|
||||
active: false,
|
||||
hasMoved: true,
|
||||
movesIndependently: false,
|
||||
drawnIndependently: true,
|
||||
allowedOutsideImage: true,
|
||||
hasBoundingBox: true,
|
||||
};
|
||||
|
||||
const anchorPoints = _getTextBoxAnchorPointsForRenderableData(
|
||||
renderableData,
|
||||
eventData
|
||||
);
|
||||
textBox.anchorPoints = anchorPoints;
|
||||
|
||||
const bottomRight = {
|
||||
x: Math.max(...anchorPoints.map(point => point.x)),
|
||||
y: Math.max(...anchorPoints.map(point => point.y)),
|
||||
};
|
||||
|
||||
textBox.x = bottomRight.x;
|
||||
textBox.y = bottomRight.y;
|
||||
|
||||
data.handles = {};
|
||||
data.handles.textBox = textBox;
|
||||
|
||||
shouldRepositionTextBoxes = true;
|
||||
}
|
||||
|
||||
const text = _getTextBoxLinesFromLabels(labels);
|
||||
|
||||
function textBoxAnchorPoints() {
|
||||
return data.handles.textBox.anchorPoints;
|
||||
}
|
||||
|
||||
if (data.handles.textBox.anchorPoints.length !== 0) {
|
||||
draw(context, context => {
|
||||
drawLinkedTextBox(
|
||||
context,
|
||||
element,
|
||||
data.handles.textBox,
|
||||
text,
|
||||
data.handles,
|
||||
textBoxAnchorPoints,
|
||||
color,
|
||||
lineWidth,
|
||||
0,
|
||||
true
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// TOOD -> text boxes may overlap with other annotations at the moment.
|
||||
// To be fixed after we get requirements.
|
||||
// if (shouldRepositionTextBoxes) {
|
||||
// this.repositionTextBox(filteredToolData, eventData);
|
||||
// }
|
||||
}
|
||||
|
||||
// repositionTextBox(toolData, eventData) {
|
||||
// const toolBoundingBoxes = [];
|
||||
|
||||
// for (let i = 0; i < toolData.length; i++) {
|
||||
// const toolDataI = toolData[i];
|
||||
|
||||
// const { textBox } = toolDataI.handles;
|
||||
// const { anchorPoints } = textBox;
|
||||
|
||||
// const boundingBox = _getBoundingBoxFromAnchorPoints(anchorPoints);
|
||||
// // Get the textbox bounding locations.
|
||||
// // Get the tool extents.
|
||||
// }
|
||||
// }
|
||||
|
||||
renderPolyLine(renderableData, eventData, options) {
|
||||
const { element } = eventData;
|
||||
const context = getNewContext(eventData.canvasContext.canvas);
|
||||
renderableData.forEach(points => {
|
||||
draw(context, context => {
|
||||
drawJoinedLines(context, element, points[0], points, options);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
renderMultipoint(renderableData, eventData, options) {
|
||||
const context = getNewContext(eventData.canvasContext.canvas);
|
||||
|
||||
renderableData.forEach(points => {
|
||||
draw(context, context => {
|
||||
drawHandles(context, eventData, points, options);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
renderPoint(renderableData, eventData, options) {
|
||||
// Render single point as an arrow.
|
||||
const { element, image } = eventData;
|
||||
const { rows, columns } = image;
|
||||
const context = getNewContext(eventData.canvasContext.canvas);
|
||||
const { color, lineWidth } = options;
|
||||
|
||||
// Find a suitable length for the image size.
|
||||
|
||||
const xOffset = columns / 10;
|
||||
const yOffset = rows / 10;
|
||||
|
||||
renderableData.forEach(points => {
|
||||
const point = points[0]; // The SCOORD type is POINT so the array length is 1.
|
||||
draw(context, context => {
|
||||
// Draw the arrow
|
||||
const handleStartCanvas = cornerstone.pixelToCanvas(element, point);
|
||||
const handleEndCanvas = cornerstone.pixelToCanvas(element, {
|
||||
x: point.x + xOffset,
|
||||
y: point.y + yOffset,
|
||||
});
|
||||
|
||||
drawArrow(
|
||||
context,
|
||||
handleEndCanvas,
|
||||
handleStartCanvas,
|
||||
color,
|
||||
lineWidth,
|
||||
false
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
renderCircle(renderableData, eventData, options) {
|
||||
const { element } = eventData;
|
||||
|
||||
const context = getNewContext(eventData.canvasContext.canvas);
|
||||
|
||||
renderableData.forEach(circle => {
|
||||
const { center, radius } = circle;
|
||||
|
||||
drawCircle(context, element, center, radius, options);
|
||||
});
|
||||
}
|
||||
|
||||
renderEllipse(renderableData, eventData, options) {
|
||||
const { element } = eventData;
|
||||
|
||||
const context = getNewContext(eventData.canvasContext.canvas);
|
||||
|
||||
renderableData.forEach(ellipse => {
|
||||
const { corner1, corner2 } = ellipse;
|
||||
|
||||
drawEllipse(
|
||||
context,
|
||||
element,
|
||||
corner1,
|
||||
corner2,
|
||||
options,
|
||||
'pixel',
|
||||
0 // TODO -> Work our the initial rotation and add it here so we render appropriately rotated ellipses.
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function _getTextBoxLinesFromLabels(labels) {
|
||||
// TODO -> max 3 for now (label + shortAxis + longAxis), need a generic solution for this!
|
||||
|
||||
const labelLength = Math.min(labels.length, 3);
|
||||
const lines = [];
|
||||
|
||||
for (let i = 0; i < labelLength; i++) {
|
||||
const labelEntry = labels[i];
|
||||
lines.push(`${_labelToShorthand(labelEntry.label)}${labelEntry.value}`);
|
||||
}
|
||||
|
||||
return lines;
|
||||
}
|
||||
|
||||
const SHORT_HAND_MAP = {
|
||||
'Short Axis': 'W ',
|
||||
'Long Axis': 'L ',
|
||||
AREA: 'Area ',
|
||||
Length: '',
|
||||
CORNERSTONEFREETEXT: '',
|
||||
};
|
||||
|
||||
function _labelToShorthand(label) {
|
||||
const shortHand = SHORT_HAND_MAP[label];
|
||||
|
||||
if (shortHand !== undefined) {
|
||||
return shortHand;
|
||||
}
|
||||
|
||||
return label;
|
||||
}
|
||||
|
||||
function _getTextBoxAnchorPointsForRenderableData(renderableData, eventData) {
|
||||
let anchorPoints = [];
|
||||
|
||||
Object.keys(renderableData).forEach(GraphicType => {
|
||||
const renderableDataForGraphicType = renderableData[GraphicType];
|
||||
|
||||
switch (GraphicType) {
|
||||
case SCOORD_TYPES.TEXT:
|
||||
break;
|
||||
case SCOORD_TYPES.POINT:
|
||||
renderableDataForGraphicType.forEach(points => {
|
||||
anchorPoints = [...anchorPoints, ...points];
|
||||
|
||||
// Add other arrow point based on image size.
|
||||
const { image } = eventData;
|
||||
const { rows, columns } = image;
|
||||
|
||||
const xOffset = columns / 10;
|
||||
const yOffset = rows / 10;
|
||||
const point = points[0];
|
||||
|
||||
anchorPoints.push({ x: point.x + xOffset, y: point.y + yOffset });
|
||||
});
|
||||
|
||||
break;
|
||||
case SCOORD_TYPES.MULTIPOINT:
|
||||
case SCOORD_TYPES.POLYLINE:
|
||||
case SCOORD_TYPES.POLYGON:
|
||||
renderableDataForGraphicType.forEach(points => {
|
||||
anchorPoints = [...anchorPoints, ...points];
|
||||
});
|
||||
break;
|
||||
case SCOORD_TYPES.CIRCLE:
|
||||
renderableDataForGraphicType.forEach(circle => {
|
||||
const { center, radius } = circle;
|
||||
|
||||
anchorPoints.push({ x: center.x + radius, y: center.y });
|
||||
anchorPoints.push({ x: center.x - radius, y: center.y });
|
||||
anchorPoints.push({ x: center.x, y: center.y + radius });
|
||||
anchorPoints.push({ x: center.x, y: center.y - radius });
|
||||
});
|
||||
|
||||
break;
|
||||
case SCOORD_TYPES.ELLIPSE:
|
||||
renderableDataForGraphicType.forEach(ellipse => {
|
||||
const { corner1, corner2 } = ellipse;
|
||||
|
||||
const halfWidth = Math.abs(corner1.x - corner2.x) / 2;
|
||||
const halfHeight = Math.abs(corner1.y - corner2.y) / 2;
|
||||
|
||||
const center = {
|
||||
x: (corner1.x + corner2.x) / 2,
|
||||
y: (corner1.y + corner2.y) / 2,
|
||||
};
|
||||
|
||||
anchorPoints.push({ x: center.x + halfWidth, y: center.y });
|
||||
anchorPoints.push({ x: center.x - halfWidth, y: center.y });
|
||||
anchorPoints.push({ x: center.x, y: center.y + halfHeight });
|
||||
anchorPoints.push({ x: center.x, y: center.y - halfHeight });
|
||||
});
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
return anchorPoints;
|
||||
}
|
||||
|
||||
function _getBoundingBoxFromAnchorPoints(anchorPoints) {
|
||||
let minX = Infinity;
|
||||
let maxX = -Infinity;
|
||||
let minY = Infinity;
|
||||
let maxY = -Infinity;
|
||||
|
||||
anchorPoints.forEach(point => {
|
||||
const { x, y } = point;
|
||||
|
||||
if (x > maxX) {
|
||||
maxX = x;
|
||||
} else if (x < minX) {
|
||||
minX = x;
|
||||
}
|
||||
|
||||
if (y > maxY) {
|
||||
maxY = y;
|
||||
} else if (y < minY) {
|
||||
minY = y;
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
import csTools from 'cornerstone-tools';
|
||||
import cs from 'cornerstone-core';
|
||||
import OHIF from '@ohif/core';
|
||||
|
||||
import { getEnabledElement } from '../state';
|
||||
import id from './id';
|
||||
|
||||
const { studyMetadataManager } = OHIF.utils;
|
||||
|
||||
const checkForSRAnnotations = ({ viewportIndex, displaySet }) => {
|
||||
const srModule = csTools.getModule(id);
|
||||
|
||||
const element = getEnabledElement(viewportIndex);
|
||||
if (!element) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { StudyInstanceUID } = displaySet;
|
||||
const studyMetadata = studyMetadataManager.get(StudyInstanceUID);
|
||||
if (!studyMetadata) {
|
||||
return;
|
||||
}
|
||||
|
||||
const srDisplaySets = studyMetadata
|
||||
.getDisplaySets()
|
||||
.filter(ds => ds.Modality === 'SR');
|
||||
if (srDisplaySets.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { measurements: _measurements } = srDisplaySets[0];
|
||||
if (!_measurements || _measurements.length < 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
const measurements = _measurements.filter(m => m.loaded === true);
|
||||
const measurement = measurements[0];
|
||||
if (!measurement) {
|
||||
return;
|
||||
}
|
||||
|
||||
srModule.setters.trackingUniqueIdentifiersForElement(
|
||||
element,
|
||||
measurements.map(measurement => measurement.TrackingUniqueIdentifier),
|
||||
measurement
|
||||
);
|
||||
|
||||
const { TrackingUniqueIdentifier } = measurement;
|
||||
srModule.setters.activeTrackingUniqueIdentifierForElement(
|
||||
element,
|
||||
TrackingUniqueIdentifier
|
||||
);
|
||||
|
||||
cs.updateImage(element);
|
||||
};
|
||||
|
||||
export default checkForSRAnnotations;
|
||||
@@ -0,0 +1,9 @@
|
||||
export default {
|
||||
TEXT: 'TEXT',
|
||||
POINT: 'POINT',
|
||||
MULTIPOINT: 'MULTIPOINT',
|
||||
POLYLINE: 'POLYLINE',
|
||||
CIRCLE: 'CIRCLE',
|
||||
ELLIPSE: 'ELLIPSE',
|
||||
POLYGON: 'POLYGON',
|
||||
};
|
||||
@@ -0,0 +1,5 @@
|
||||
const TOOL_NAMES = {
|
||||
DICOM_SR_DISPLAY_TOOL: 'DICOMSRDisplayTool',
|
||||
};
|
||||
|
||||
export default TOOL_NAMES;
|
||||
@@ -0,0 +1,7 @@
|
||||
const id = 'org.ohif.dicom-sr';
|
||||
|
||||
export default id;
|
||||
|
||||
const SOPClassHandlerName = 'dicom-sr';
|
||||
const SOPClassHandlerId = `${id}.sopClassHandlerModule.${SOPClassHandlerName}`;
|
||||
export { SOPClassHandlerName, SOPClassHandlerId };
|
||||
@@ -0,0 +1,108 @@
|
||||
import cornerstoneTools from 'cornerstone-tools';
|
||||
import DICOMSRDisplayTool from './DICOMSRDisplayTool';
|
||||
import TOOL_NAMES from './constants/toolNames';
|
||||
import getToolAlias from './utils/getToolAlias';
|
||||
|
||||
/**
|
||||
* Initialize SR cornerstone tools.
|
||||
*
|
||||
* @param {*} targetElement
|
||||
*/
|
||||
const initSRTools = targetElement => {
|
||||
const primaryToolId = 'Wwwc';
|
||||
const toolAlias = getToolAlias(primaryToolId); // These are 1:1 for built-in only
|
||||
|
||||
// ~~ MAGIC
|
||||
cornerstoneTools.addToolForElement(targetElement, DICOMSRDisplayTool);
|
||||
cornerstoneTools.setToolEnabledForElement(
|
||||
targetElement,
|
||||
TOOL_NAMES.DICOM_SR_DISPLAY_TOOL
|
||||
);
|
||||
|
||||
// ~~ Variants
|
||||
cornerstoneTools.addToolForElement(
|
||||
targetElement,
|
||||
cornerstoneTools.LengthTool,
|
||||
{
|
||||
name: 'SRLength',
|
||||
configuration: {
|
||||
renderDashed: true,
|
||||
},
|
||||
}
|
||||
);
|
||||
cornerstoneTools.addToolForElement(
|
||||
targetElement,
|
||||
cornerstoneTools.ArrowAnnotateTool,
|
||||
{
|
||||
name: 'SRArrowAnnotate',
|
||||
configuration: {
|
||||
renderDashed: true,
|
||||
},
|
||||
}
|
||||
);
|
||||
cornerstoneTools.addToolForElement(
|
||||
targetElement,
|
||||
cornerstoneTools.BidirectionalTool,
|
||||
{
|
||||
name: 'SRBidirectional',
|
||||
configuration: {
|
||||
renderDashed: true,
|
||||
},
|
||||
}
|
||||
);
|
||||
cornerstoneTools.addToolForElement(
|
||||
targetElement,
|
||||
cornerstoneTools.EllipticalRoiTool,
|
||||
{
|
||||
name: 'SREllipticalRoi',
|
||||
configuration: {
|
||||
renderDashed: true,
|
||||
},
|
||||
}
|
||||
);
|
||||
cornerstoneTools.addToolForElement(
|
||||
targetElement,
|
||||
cornerstoneTools.RectangleRoiTool,
|
||||
{
|
||||
name: 'SRRectangleRoi',
|
||||
configuration: {
|
||||
renderDashed: true,
|
||||
},
|
||||
}
|
||||
);
|
||||
cornerstoneTools.addToolForElement(
|
||||
targetElement,
|
||||
cornerstoneTools.FreehandRoiTool,
|
||||
{
|
||||
name: 'SRFreehandRoi',
|
||||
configuration: {
|
||||
renderDashed: true,
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
// ~~ Business as usual
|
||||
cornerstoneTools.setToolActiveForElement(targetElement, 'PanMultiTouch', {
|
||||
pointers: 2,
|
||||
});
|
||||
cornerstoneTools.setToolActiveForElement(targetElement, 'ZoomTouchPinch', {});
|
||||
|
||||
// TODO: Add always dashed tool alternative aliases
|
||||
// TODO: or same name... alternative config?
|
||||
cornerstoneTools.setToolActiveForElement(targetElement, toolAlias, {
|
||||
mouseButtonMask: 1,
|
||||
});
|
||||
cornerstoneTools.setToolActiveForElement(targetElement, 'Pan', {
|
||||
mouseButtonMask: 4,
|
||||
});
|
||||
cornerstoneTools.setToolActiveForElement(targetElement, 'Zoom', {
|
||||
mouseButtonMask: 2,
|
||||
});
|
||||
cornerstoneTools.setToolActiveForElement(
|
||||
targetElement,
|
||||
'StackScrollMouseWheel',
|
||||
{}
|
||||
);
|
||||
};
|
||||
|
||||
export default initSRTools;
|
||||
@@ -0,0 +1,61 @@
|
||||
import cornerstone from 'cornerstone-core';
|
||||
|
||||
const state = {
|
||||
TrackingUniqueIdentifier: null,
|
||||
trackingIdentifiersByEnabledElementUUID: {},
|
||||
};
|
||||
|
||||
function setTrackingUniqueIdentifiersForElement(
|
||||
element,
|
||||
trackingUniqueIdentifiers,
|
||||
activeIndex = 0
|
||||
) {
|
||||
const enabledElement = cornerstone.getEnabledElement(element);
|
||||
const { uuid } = enabledElement;
|
||||
|
||||
state.trackingIdentifiersByEnabledElementUUID[uuid] = {
|
||||
trackingUniqueIdentifiers,
|
||||
activeIndex,
|
||||
};
|
||||
}
|
||||
|
||||
function setActiveTrackingUniqueIdentifierForElement(
|
||||
element,
|
||||
TrackingUniqueIdentifier
|
||||
) {
|
||||
const enabledElement = cornerstone.getEnabledElement(element);
|
||||
const { uuid } = enabledElement;
|
||||
|
||||
const trackingIdentifiersForElement =
|
||||
state.trackingIdentifiersByEnabledElementUUID[uuid];
|
||||
|
||||
if (trackingIdentifiersForElement) {
|
||||
const activeIndex = trackingIdentifiersForElement.trackingUniqueIdentifiers.findIndex(
|
||||
tuid => tuid === TrackingUniqueIdentifier
|
||||
);
|
||||
|
||||
trackingIdentifiersForElement.activeIndex = activeIndex;
|
||||
}
|
||||
}
|
||||
|
||||
function getTrackingUniqueIdentifiersForElement(element) {
|
||||
const enabledElement = cornerstone.getEnabledElement(element);
|
||||
const { uuid } = enabledElement;
|
||||
|
||||
if (state.trackingIdentifiersByEnabledElementUUID[uuid]) {
|
||||
return state.trackingIdentifiersByEnabledElementUUID[uuid];
|
||||
}
|
||||
|
||||
return { trackingUniqueIdentifiers: [] };
|
||||
}
|
||||
|
||||
export default {
|
||||
state,
|
||||
getters: {
|
||||
trackingUniqueIdentifiersForElement: getTrackingUniqueIdentifiersForElement,
|
||||
},
|
||||
setters: {
|
||||
trackingUniqueIdentifiersForElement: setTrackingUniqueIdentifiersForElement,
|
||||
activeTrackingUniqueIdentifierForElement: setActiveTrackingUniqueIdentifierForElement,
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,32 @@
|
||||
/**
|
||||
* Get cornerstone tool alias.
|
||||
*
|
||||
* @param {string} toolName
|
||||
* @returns tool alias
|
||||
*/
|
||||
export default function getToolAlias(toolName) {
|
||||
let toolAlias = toolName;
|
||||
|
||||
switch (toolName) {
|
||||
case 'Length':
|
||||
toolAlias = 'SRLength';
|
||||
break;
|
||||
case 'Bidirectional':
|
||||
toolAlias = 'SRBidirectional';
|
||||
break;
|
||||
case 'ArrowAnnotate':
|
||||
toolAlias = 'SRArrowAnnotate';
|
||||
break;
|
||||
case 'EllipticalRoi':
|
||||
toolAlias = 'SREllipticalRoi';
|
||||
break;
|
||||
case 'FreehandRoi':
|
||||
toolAlias = 'SRFreehandRoi';
|
||||
break;
|
||||
case 'RectangleRoi':
|
||||
toolAlias = 'SRRectangleRoi';
|
||||
break;
|
||||
}
|
||||
|
||||
return toolAlias;
|
||||
}
|
||||
@@ -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
|
||||
};
|
||||
File renamed without changes.
File renamed without changes.
@@ -0,0 +1,138 @@
|
||||
# Change Log
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [0.3.3](https://github.com/OHIF/Viewers/compare/@ohif/extension-debugging@0.3.2...@ohif/extension-debugging@0.3.3) (2022-06-30)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-debugging
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [0.3.2](https://github.com/OHIF/Viewers/compare/@ohif/extension-debugging@0.3.1...@ohif/extension-debugging@0.3.2) (2022-03-10)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-debugging
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [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)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-debugging
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [0.1.5](https://github.com/OHIF/Viewers/compare/@ohif/extension-debugging@0.1.4...@ohif/extension-debugging@0.1.5) (2020-09-17)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-debugging
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [0.1.4](https://github.com/OHIF/Viewers/compare/@ohif/extension-debugging@0.1.3...@ohif/extension-debugging@0.1.4) (2020-09-10)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-debugging
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## 0.1.3 (2020-09-03)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-debugging
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [0.1.2](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-p10-downloader@0.1.1...@ohif/extension-dicom-p10-downloader@0.1.2) (2020-06-18)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-dicom-p10-downloader
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [0.1.1](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-p10-downloader@0.1.0...@ohif/extension-dicom-p10-downloader@0.1.1) (2020-06-04)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-dicom-p10-downloader
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [0.1.0](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-p10-downloader@0.0.2...@ohif/extension-dicom-p10-downloader@0.1.0) (2020-04-23)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* configuration to hook into XHR Error handling ([e96205d](https://github.com/OHIF/Viewers/commit/e96205de35e5bec14dc8a9a8509db3dd4e6ecdb6))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## 0.0.2 (2020-04-15)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-dicom-p10-downloader
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# Change Log
|
||||
|
||||
All notable changes to this project will be documented in this file. See
|
||||
[Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
File renamed without changes.
File renamed without changes.
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@ohif/extension-dicom-p10-downloader",
|
||||
"version": "0.1.0",
|
||||
"description": "OHIF extension for downloading DICOM P10 files",
|
||||
"name": "@ohif/extension-debugging",
|
||||
"version": "0.3.3",
|
||||
"description": "OHIF extension for debugging.",
|
||||
"author": "OHIF",
|
||||
"license": "MIT",
|
||||
"repository": "OHIF/Viewers",
|
||||
@@ -28,12 +28,12 @@
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@ohif/core": "^2.6.0",
|
||||
"dicom-parser": "^1.8.3",
|
||||
"dicomweb-client": "^0.5.2"
|
||||
"dicom-parser": "^1.8.11"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.5.5",
|
||||
"dicomweb-client": "^0.6.0",
|
||||
"detect-browser": "5.1.1",
|
||||
"dicomweb-client": "^0.8.3",
|
||||
"file-saver": "^2.0.2",
|
||||
"jszip": "^3.2.2"
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
.debugReportModalHeader {
|
||||
padding: 10px 0 10px;
|
||||
color: var(--active-color);
|
||||
}
|
||||
|
||||
.debug-report-modal-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.debug-report-modal-buttons-container {
|
||||
display: flex;
|
||||
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;
|
||||
}
|
||||
|
||||
.debug-overflowText {
|
||||
margin: 0 auto;
|
||||
max-width: 300px;
|
||||
overflow-wrap: break-word;
|
||||
word-wrap: break-word;
|
||||
hyphens: auto;
|
||||
}
|
||||
|
||||
.debug-overflowText-border {
|
||||
margin: 0 auto;
|
||||
max-width: 300px;
|
||||
border: solid 2px #ccc;
|
||||
padding: 12px;
|
||||
overflow-wrap: break-word;
|
||||
word-wrap: break-word;
|
||||
hyphens: auto;
|
||||
}
|
||||
@@ -0,0 +1,439 @@
|
||||
import React from 'react';
|
||||
import { detect } from 'detect-browser';
|
||||
import './DebugReportModal.css';
|
||||
import { ToolbarButton } from '@ohif/ui';
|
||||
import { utils } from '@ohif/core';
|
||||
const { studyMetadataManager } = utils;
|
||||
|
||||
const DubugReportModal = ({
|
||||
viewports,
|
||||
studies,
|
||||
servers,
|
||||
extensionManager,
|
||||
mailTo,
|
||||
debugModalMessage,
|
||||
errors = [],
|
||||
}) => {
|
||||
const copyDebugDataToClipboard = () => {
|
||||
const body = getEmailBody();
|
||||
|
||||
const textArea = document.createElement('textarea');
|
||||
|
||||
textArea.value = body;
|
||||
document.body.appendChild(textArea);
|
||||
textArea.select();
|
||||
document.execCommand('copy');
|
||||
document.body.removeChild(textArea);
|
||||
};
|
||||
|
||||
const getEmailBody = () => {
|
||||
let body = `Enter the description of your problem here: \n\n\n`;
|
||||
|
||||
body += `============= SESSION INFO =============\n\n`;
|
||||
|
||||
// App version
|
||||
|
||||
body += '== App ==\n';
|
||||
body += `version\t${window.version}\n\n`;
|
||||
|
||||
// Extensions Versions
|
||||
|
||||
body += '== Extensions Versions ==\n';
|
||||
|
||||
const { registeredExtensionVesions } = extensionManager;
|
||||
|
||||
Object.keys(registeredExtensionVesions).forEach(extensionId => {
|
||||
const version = registeredExtensionVesions[extensionId];
|
||||
|
||||
body += `${extensionId}\t${version}\n`;
|
||||
});
|
||||
|
||||
body += '\n';
|
||||
|
||||
// Browser Info
|
||||
|
||||
const browser = detect();
|
||||
|
||||
const { name, os, type, version } = browser;
|
||||
|
||||
body += '== Browser Info ==\n';
|
||||
body += `name\t ${name}\n`;
|
||||
body += `os\t ${os}\n`;
|
||||
body += `type\t ${type}\n`;
|
||||
body += `version\t ${version}\n\n`;
|
||||
|
||||
// Study URL
|
||||
body += '== URL ==\n';
|
||||
body += `URL\t ${window.location.href}\n\n`;
|
||||
|
||||
// Layout
|
||||
const { numRows, numColumns, viewportSpecificData } = viewports;
|
||||
|
||||
body += '== Viewport Layout ==\n';
|
||||
body += `Rows\t${numRows}\n`;
|
||||
body += `Columns\t${numColumns}\n\n`;
|
||||
body += '== SeriesInstanceUIDs ==\n';
|
||||
|
||||
Object.keys(viewportSpecificData).forEach(viewportIndex => {
|
||||
const vsd = viewportSpecificData[viewportIndex];
|
||||
|
||||
const [row, column] = _viewportIndexToViewportPosition(
|
||||
viewportIndex,
|
||||
numColumns
|
||||
);
|
||||
|
||||
body += `[${row},${column}]\t${vsd.SeriesInstanceUID}\n`;
|
||||
});
|
||||
|
||||
body += '== ReferencedSEGSeriesInstanceUIDs ==\n';
|
||||
|
||||
Object.keys(viewportSpecificData).forEach(viewportIndex => {
|
||||
const displaySet = viewportSpecificData[viewportIndex];
|
||||
const [referencedDisplaySetsRef] = _getReferencedSeriesInstanceUIDsString(
|
||||
displaySet,
|
||||
'SEG'
|
||||
);
|
||||
|
||||
const [row, column] = _viewportIndexToViewportPosition(
|
||||
viewportIndex,
|
||||
numColumns
|
||||
);
|
||||
|
||||
body += `[${row},${column}]\t${referencedDisplaySetsRef}\n`;
|
||||
});
|
||||
|
||||
body += '== ReferencedRTSTRUCTSeriesInstanceUIDs ==\n';
|
||||
|
||||
Object.keys(viewportSpecificData).forEach(viewportIndex => {
|
||||
const displaySet = viewportSpecificData[viewportIndex];
|
||||
const [referencedDisplaySetsRef] = _getReferencedSeriesInstanceUIDsString(
|
||||
displaySet,
|
||||
'RTSTRUCT'
|
||||
);
|
||||
|
||||
const [row, column] = _viewportIndexToViewportPosition(
|
||||
viewportIndex,
|
||||
numColumns
|
||||
);
|
||||
|
||||
body += `[${row},${column}]\t${referencedDisplaySetsRef}\n`;
|
||||
});
|
||||
|
||||
body += '== ReferencedSRSeriesInstanceUIDs ==\n';
|
||||
|
||||
Object.keys(viewportSpecificData).forEach(viewportIndex => {
|
||||
const displaySet = viewportSpecificData[viewportIndex];
|
||||
const [referencedDisplaySetsRef] = _getReferencedSeriesInstanceUIDsString(
|
||||
displaySet,
|
||||
'SR'
|
||||
);
|
||||
|
||||
const [row, column] = _viewportIndexToViewportPosition(
|
||||
viewportIndex,
|
||||
numColumns
|
||||
);
|
||||
|
||||
body += `[${row},${column}]\t${referencedDisplaySetsRef}\n`;
|
||||
});
|
||||
|
||||
return body;
|
||||
};
|
||||
|
||||
const mailToFunction = () => {
|
||||
const StudyInstanceUID = Object.keys(studies.studyData)[0];
|
||||
|
||||
const subject = encodeURI(`Issue with Study: ${StudyInstanceUID}`);
|
||||
|
||||
let body = getEmailBody();
|
||||
|
||||
// TODO Text dump of rest of stuff.
|
||||
|
||||
body = encodeURI(body);
|
||||
|
||||
window.location.href = `mailto:${mailTo}?subject=${subject}&body=${body}`;
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="debug-report-modal-container">
|
||||
{debugModalMessage ? <p>{debugModalMessage}</p> : null}
|
||||
<div className="debug-report-modal-buttons-container">
|
||||
{mailTo ? (
|
||||
<div>
|
||||
<ToolbarButton
|
||||
label={'Send Bug Report'}
|
||||
onClick={mailToFunction}
|
||||
icon={'envelope-square'}
|
||||
isActive={false}
|
||||
/>
|
||||
</div>
|
||||
) : null}
|
||||
<div>
|
||||
<ToolbarButton
|
||||
label={'Copy To Clipboard'}
|
||||
onClick={copyDebugDataToClipboard}
|
||||
icon={'clipboard'}
|
||||
isActive={false}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<table>
|
||||
{getAppVersion()}
|
||||
{getExtensionVersions(extensionManager)}
|
||||
{getBrowserInfo()}
|
||||
{getCurrentStudyUrl()}
|
||||
{getLayout(viewports)}
|
||||
</table>
|
||||
</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>
|
||||
);
|
||||
};
|
||||
|
||||
const getAppVersion = () => {
|
||||
return (
|
||||
<React.Fragment>
|
||||
<tr>
|
||||
<th className="debugReportModalHeader">App</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Version</td>
|
||||
<td>{window.version}</td>
|
||||
</tr>
|
||||
</React.Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
const getCurrentStudyUrl = () => {
|
||||
return (
|
||||
<React.Fragment>
|
||||
<tr>
|
||||
<th className="debugReportModalHeader">URL</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>URL</td>
|
||||
<td className="debug-overflowText">{window.location.href}</td>
|
||||
</tr>
|
||||
</React.Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
const getExtensionVersions = extensionManager => {
|
||||
const { registeredExtensionVesions } = extensionManager;
|
||||
|
||||
const lineItems = Object.keys(registeredExtensionVesions).map(extensionId => {
|
||||
const version = registeredExtensionVesions[extensionId];
|
||||
|
||||
return (
|
||||
<tr>
|
||||
<td>{extensionId}</td>
|
||||
<td>{version}</td>
|
||||
</tr>
|
||||
);
|
||||
});
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<th className="debugReportModalHeader">Extensions</th>
|
||||
{lineItems}
|
||||
</React.Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
const getLayout = viewports => {
|
||||
const { numRows, numColumns } = viewports;
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<tr>
|
||||
<th className="debugReportModalHeader">Viewports</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Layout</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Rows</td>
|
||||
<td>{numRows}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Columns</td>
|
||||
<td>{numColumns}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>SeriesInstanceUIDs</th>
|
||||
</tr>
|
||||
{getSeriesInstanceUIDsPerRow(viewports)}
|
||||
<tr>
|
||||
<th>ReferencedSEGSeriesInstanceUIDs</th>
|
||||
</tr>
|
||||
{getReferencedSeriesInstanceUIDsPerRow(viewports, 'SEG')}
|
||||
<tr>
|
||||
<th>ReferencedRTSTRUCTSeriesInstanceUIDs</th>
|
||||
</tr>
|
||||
{getReferencedSeriesInstanceUIDsPerRow(viewports, 'RTSTRUCT')}
|
||||
<tr>
|
||||
<th>ReferencedSRSeriesInstanceUIDs</th>
|
||||
</tr>
|
||||
{getReferencedSeriesInstanceUIDsPerRow(viewports, 'SR')}
|
||||
</React.Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
const getBrowserInfo = () => {
|
||||
const browser = detect();
|
||||
|
||||
const { name, os, type, version } = browser;
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<tr>
|
||||
<th className="debugReportModalHeader">Platform</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>name</td>
|
||||
<td>{name}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>os</td>
|
||||
<td>{os}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>type</td>
|
||||
<td>{type}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>version</td>
|
||||
<td>{version}</td>
|
||||
</tr>
|
||||
</React.Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns DisplaySets that reference the target series, sorted by dateTime
|
||||
*
|
||||
* @param {string} StudyInstanceUID
|
||||
* @param {string} SeriesInstanceUID
|
||||
* @returns Array
|
||||
*/
|
||||
const _getReferencedDisplaysets = (
|
||||
StudyInstanceUID,
|
||||
SeriesInstanceUID,
|
||||
Modality
|
||||
) => {
|
||||
/* Referenced DisplaySets */
|
||||
const studyMetadata = studyMetadataManager.get(StudyInstanceUID);
|
||||
const referencedDisplaysets = studyMetadata.getDerivedDatasets({
|
||||
referencedSeriesInstanceUID: SeriesInstanceUID,
|
||||
Modality,
|
||||
});
|
||||
|
||||
/* Sort */
|
||||
referencedDisplaysets.sort((a, b) => {
|
||||
const aNumber = Number(`${a.SeriesDate}${a.SeriesTime}`);
|
||||
const bNumber = Number(`${b.SeriesDate}${b.SeriesTime}`);
|
||||
return bNumber - aNumber;
|
||||
});
|
||||
|
||||
return referencedDisplaysets;
|
||||
};
|
||||
|
||||
const _getReferencedSeriesInstanceUIDsString = (displaySet, Modality) => {
|
||||
const referencedDisplaySets = _getReferencedDisplaysets(
|
||||
displaySet.StudyInstanceUID,
|
||||
displaySet.SeriesInstanceUID,
|
||||
Modality
|
||||
);
|
||||
|
||||
let referencedDisplaySetsRef = 'None';
|
||||
const referencedDisplaySetsCount = referencedDisplaySets.length;
|
||||
if (referencedDisplaySetsCount !== 0) {
|
||||
referencedDisplaySetsRef = '';
|
||||
for (let i = 0; i < referencedDisplaySetsCount - 1; i++) {
|
||||
referencedDisplaySetsRef +=
|
||||
referencedDisplaySets[i].SeriesInstanceUID + ', ';
|
||||
}
|
||||
referencedDisplaySetsRef +=
|
||||
referencedDisplaySets[referencedDisplaySetsCount - 1].SeriesInstanceUID;
|
||||
}
|
||||
|
||||
return [referencedDisplaySetsRef, referencedDisplaySetsCount];
|
||||
};
|
||||
|
||||
const getReferencedSeriesInstanceUIDsPerRow = (viewports, Modality) => {
|
||||
const { viewportSpecificData, numColumns } = viewports;
|
||||
|
||||
// NOTE viewportSpecificData is actually an object with numerical keys.
|
||||
return Object.keys(viewportSpecificData).map(viewportIndex => {
|
||||
const displaySet = viewportSpecificData[viewportIndex];
|
||||
const [
|
||||
referencedDisplaySetsRef,
|
||||
referencedDisplaySetsCount,
|
||||
] = _getReferencedSeriesInstanceUIDsString(displaySet, Modality);
|
||||
|
||||
const [row, column] = _viewportIndexToViewportPosition(
|
||||
viewportIndex,
|
||||
numColumns
|
||||
);
|
||||
|
||||
if (referencedDisplaySetsCount > 1) {
|
||||
return (
|
||||
<tr>
|
||||
<td>{`[${row},${column}]`}</td>
|
||||
<td className="debug-overflowText-border">
|
||||
{referencedDisplaySetsRef}
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<tr>
|
||||
<td>{`[${row},${column}]`}</td>
|
||||
<td className="debug-overflowText">{referencedDisplaySetsRef}</td>
|
||||
</tr>
|
||||
);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const getSeriesInstanceUIDsPerRow = viewports => {
|
||||
const { viewportSpecificData, numColumns } = viewports;
|
||||
|
||||
// NOTE viewportSpecificData is actually an object with numerical keys.
|
||||
return Object.keys(viewportSpecificData).map(viewportIndex => {
|
||||
const vsd = viewportSpecificData[viewportIndex];
|
||||
|
||||
const [row, column] = _viewportIndexToViewportPosition(
|
||||
viewportIndex,
|
||||
numColumns
|
||||
);
|
||||
|
||||
return (
|
||||
<tr>
|
||||
<td>{`[${row},${column}]`}</td>
|
||||
<td>{vsd.SeriesInstanceUID}</td>
|
||||
</tr>
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
const _viewportIndexToViewportPosition = (viewportIndex, numColumns) => {
|
||||
const row = Math.floor(viewportIndex / numColumns);
|
||||
const column = viewportIndex % numColumns;
|
||||
|
||||
return [row, column];
|
||||
};
|
||||
|
||||
export default DubugReportModal;
|
||||
+78
-5
@@ -1,19 +1,25 @@
|
||||
import OHIF from '@ohif/core';
|
||||
import { useLogger } from '@ohif/ui';
|
||||
|
||||
import {
|
||||
save,
|
||||
upload,
|
||||
getDicomWebClientFromContext,
|
||||
getStudyInstanceUIDFromStudies,
|
||||
getSOPInstanceReferenceFromActiveViewport,
|
||||
getSOPInstanceReferencesFromViewports,
|
||||
} from './utils';
|
||||
import _downloadAndZip from './downloadAndZip';
|
||||
import _downloadAndZip, { downloadInstances } from './downloadAndZip';
|
||||
import DebugReportModal from './DebugReportModal';
|
||||
import React from 'react';
|
||||
|
||||
import state from './state';
|
||||
|
||||
const {
|
||||
utils: {
|
||||
Queue,
|
||||
},
|
||||
utils: { Queue },
|
||||
} = OHIF;
|
||||
|
||||
export function getCommands(context) {
|
||||
export function getCommands(context, servicesManager, extensionManager) {
|
||||
const queue = new Queue(1);
|
||||
const actions = {
|
||||
/**
|
||||
@@ -41,6 +47,14 @@ export function getCommands(context) {
|
||||
listOfUIDs
|
||||
);
|
||||
},
|
||||
downloadAndZipStudy({ servers, studies, progress }) {
|
||||
const dicomWebClient = getDicomWebClientFromContext(context, servers);
|
||||
const listOfUIDs = getStudyInstanceUIDFromStudies(studies);
|
||||
return save(
|
||||
_downloadAndZip(dicomWebClient, listOfUIDs, { progress }),
|
||||
listOfUIDs
|
||||
);
|
||||
},
|
||||
downloadAndZipSeriesOnViewports({ servers, viewports, progress }) {
|
||||
const dicomWebClient = getDicomWebClientFromContext(context, servers);
|
||||
const listOfUIDs = getSOPInstanceReferencesFromViewports(viewports);
|
||||
@@ -57,13 +71,67 @@ export function getCommands(context) {
|
||||
listOfUIDs
|
||||
);
|
||||
},
|
||||
downloadAndUploadStudy({ servers, studies, progress, serverConfig }) {
|
||||
const dicomWebClient = getDicomWebClientFromContext(context, servers);
|
||||
const listOfUIDs = getStudyInstanceUIDFromStudies(studies);
|
||||
return upload(
|
||||
downloadInstances(dicomWebClient, listOfUIDs, { progress }),
|
||||
/**
|
||||
* serverConfig is an object with the values used to create a new
|
||||
* instance of DICOMwebClient.
|
||||
*
|
||||
* Basic Structure:
|
||||
*
|
||||
* const config = {
|
||||
* url,
|
||||
* headers,
|
||||
* errorInterceptor
|
||||
* }
|
||||
*
|
||||
* const dicomWeb = new api.DICOMwebClient(config);
|
||||
*/
|
||||
serverConfig
|
||||
);
|
||||
},
|
||||
openDebugInfoModal({ viewports, studies, servers }) {
|
||||
const { UIModalService } = servicesManager.services;
|
||||
|
||||
const WrappedDebugReportModal = function() {
|
||||
const { state: loggerState } = useLogger();
|
||||
return (
|
||||
<DebugReportModal
|
||||
viewports={viewports}
|
||||
studies={studies}
|
||||
servers={servers}
|
||||
extensionManager={extensionManager}
|
||||
mailTo={state.mailTo}
|
||||
debugModalMessage={state.debugModalMessage}
|
||||
errors={loggerState.errors}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
UIModalService.show({
|
||||
content: WrappedDebugReportModal,
|
||||
title: `Debugging Information`,
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
const definitions = {
|
||||
openDebugInfoModal: {
|
||||
commandFn: actions.openDebugInfoModal,
|
||||
storeContexts: ['viewports', 'servers', 'studies'],
|
||||
},
|
||||
downloadAndZip: {
|
||||
commandFn: queue.bindSafe(actions.downloadAndZip, error),
|
||||
storeContexts: ['servers'],
|
||||
},
|
||||
downloadAndZipStudy: {
|
||||
commandFn: queue.bindSafe(actions.downloadAndZipStudy, error),
|
||||
storeContexts: ['servers', 'studies'],
|
||||
options: { progress },
|
||||
},
|
||||
downloadAndZipSeriesOnViewports: {
|
||||
commandFn: queue.bindSafe(actions.downloadAndZipSeriesOnViewports, error),
|
||||
storeContexts: ['servers', 'viewports'],
|
||||
@@ -77,6 +145,11 @@ export function getCommands(context) {
|
||||
storeContexts: ['servers', 'viewports'],
|
||||
options: { progress },
|
||||
},
|
||||
downloadAndUploadStudy: {
|
||||
commandFn: queue.bindSafe(actions.downloadAndUploadStudy, error),
|
||||
storeContexts: ['servers', 'studies'],
|
||||
options: { progress },
|
||||
},
|
||||
};
|
||||
|
||||
return {
|
||||
+23
-8
@@ -79,13 +79,7 @@ async function downloadAndZip(dicomWebClient, listOfUIDs, options) {
|
||||
if (dicomWebClient instanceof api.DICOMwebClient) {
|
||||
const settings = buildSettings(listOfUIDs, options);
|
||||
const { compression } = settings.tasks;
|
||||
// Register user-provided progress handler as a task list observer
|
||||
progressUtils.addObserver(settings.taskList, settings.options.progress);
|
||||
const buffers = await downloadAll(dicomWebClient, settings).catch(error => {
|
||||
// Reject promise from compression task on download failure
|
||||
compression.deferred.reject(error);
|
||||
throw error;
|
||||
});
|
||||
const buffers = await downloadBuffers(settings, dicomWebClient);
|
||||
compression.deferred.resolve(zipAll(buffers, settings));
|
||||
const url = await compression.deferred.promise;
|
||||
return url;
|
||||
@@ -93,6 +87,27 @@ async function downloadAndZip(dicomWebClient, listOfUIDs, options) {
|
||||
throw new Error('A valid DICOM Web Client instance is expected');
|
||||
}
|
||||
|
||||
async function downloadInstances(dicomWebClient, listOfUIDs, options) {
|
||||
if (dicomWebClient instanceof api.DICOMwebClient) {
|
||||
const settings = buildSettings(listOfUIDs, options);
|
||||
const buffers = await downloadBuffers(settings, dicomWebClient);
|
||||
return buffers;
|
||||
}
|
||||
throw new Error('A valid DICOM Web Client instance is expected');
|
||||
}
|
||||
|
||||
async function downloadBuffers(settings, dicomWebClient) {
|
||||
const { compression } = settings.tasks;
|
||||
// Register user-provided progress handler as a task list observer
|
||||
progressUtils.addObserver(settings.taskList, settings.options.progress);
|
||||
const buffers = await downloadAll(dicomWebClient, settings).catch(error => {
|
||||
// Reject promise from compression task on download failure
|
||||
compression.deferred.reject(error);
|
||||
throw error;
|
||||
});
|
||||
return buffers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Utils
|
||||
*/
|
||||
@@ -241,4 +256,4 @@ async function download(
|
||||
* Exports
|
||||
*/
|
||||
|
||||
export { downloadAndZip as default, downloadAndZip };
|
||||
export { downloadAndZip as default, downloadAndZip, downloadInstances };
|
||||
@@ -0,0 +1,61 @@
|
||||
import { getDicomWebClientFromConfig } from './utils';
|
||||
import { getCommands } from './commandsModule';
|
||||
import { version } from '../package.json';
|
||||
import toolbarModule from './toolbarModule';
|
||||
import state from './state';
|
||||
|
||||
/**
|
||||
* Constants
|
||||
*/
|
||||
|
||||
/**
|
||||
* Globals
|
||||
*/
|
||||
|
||||
const sharedContext = {
|
||||
dicomWebClient: null,
|
||||
};
|
||||
|
||||
/**
|
||||
* Extension
|
||||
*/
|
||||
export default {
|
||||
/**
|
||||
* Only required property. Should be a unique value across all extensions.
|
||||
*/
|
||||
id: 'dicom-p10-downloader',
|
||||
version,
|
||||
|
||||
/**
|
||||
* LIFECYCLE HOOKS
|
||||
*/
|
||||
|
||||
preRegistration({ appConfig, configuration }) {
|
||||
const dicomWebClient = getDicomWebClientFromConfig(appConfig);
|
||||
if (dicomWebClient) {
|
||||
sharedContext.dicomWebClient = dicomWebClient;
|
||||
}
|
||||
|
||||
if (configuration) {
|
||||
if (configuration.mailTo) {
|
||||
state.mailTo = configuration.mailTo;
|
||||
}
|
||||
|
||||
if (configuration.debugModalMessage) {
|
||||
state.debugModalMessage = configuration.debugModalMessage;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* MODULE GETTERS
|
||||
*/
|
||||
|
||||
getCommandsModule({ servicesManager, extensionManager }) {
|
||||
return getCommands(sharedContext, servicesManager, extensionManager);
|
||||
},
|
||||
|
||||
getToolbarModule() {
|
||||
return toolbarModule;
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,3 @@
|
||||
const state = { mailTo: undefined, debugModalMessage: undefined };
|
||||
|
||||
export default state;
|
||||
@@ -0,0 +1,20 @@
|
||||
const TOOLBAR_BUTTON_TYPES = {
|
||||
COMMAND: 'command',
|
||||
};
|
||||
|
||||
const definitions = [
|
||||
{
|
||||
id: 'Debug Info',
|
||||
label: 'Debug Info',
|
||||
icon: 'cog',
|
||||
//
|
||||
type: TOOLBAR_BUTTON_TYPES.COMMAND,
|
||||
commandName: 'openDebugInfoModal',
|
||||
context: 'VIEWER',
|
||||
},
|
||||
];
|
||||
|
||||
export default {
|
||||
definitions,
|
||||
defaultContext: 'VIEWER',
|
||||
};
|
||||
@@ -100,11 +100,59 @@ function save(promise, listOfUIDs) {
|
||||
});
|
||||
}
|
||||
|
||||
function upload(promise, serverConfig) {
|
||||
return Promise.resolve(promise)
|
||||
.then(async instances => {
|
||||
const instancesAmount = instances.length;
|
||||
OHIF.log.info(`Uploading study to ${serverConfig.url}`);
|
||||
OHIF.log.info(
|
||||
`${instancesAmount} instances are being uploaded. Don't close your browser.`
|
||||
);
|
||||
|
||||
try {
|
||||
const dicomWeb = new api.DICOMwebClient(serverConfig);
|
||||
let progress = 0;
|
||||
|
||||
const getProgress = () => {
|
||||
return ((progress * 100) / instancesAmount).toFixed();
|
||||
};
|
||||
|
||||
for (const instance of instances) {
|
||||
const options = {
|
||||
datasets: [instance],
|
||||
};
|
||||
|
||||
await dicomWeb.storeInstances(options);
|
||||
|
||||
progress++;
|
||||
|
||||
OHIF.log.info(`Progress: ${getProgress()}%`);
|
||||
}
|
||||
|
||||
OHIF.log.info('Successfully uploaded!');
|
||||
} catch (error) {
|
||||
OHIF.log.error(`Failed to upload: ${error}`);
|
||||
}
|
||||
|
||||
return instances;
|
||||
})
|
||||
.catch(error => {
|
||||
OHIF.log.error(`Failed to upload: ${error}`);
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
function getStudyInstanceUIDFromStudies(studies) {
|
||||
return Object.keys(Object(Object(studies).studyData)).slice(0, 1);
|
||||
}
|
||||
|
||||
export {
|
||||
save,
|
||||
upload,
|
||||
validDicomUid,
|
||||
getDicomWebClientFromConfig,
|
||||
getDicomWebClientFromContext,
|
||||
getStudyInstanceUIDFromStudies,
|
||||
getSOPInstanceReferenceFromActiveViewport,
|
||||
getSOPInstanceReferencesFromViewports,
|
||||
};
|
||||
@@ -3,6 +3,290 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.3.24](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-html@1.3.23...@ohif/extension-dicom-html@1.3.24) (2022-12-21)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-dicom-html
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [1.3.23](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-html@1.3.22...@ohif/extension-dicom-html@1.3.23) (2022-09-01)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-dicom-html
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [1.3.22](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-html@1.3.21...@ohif/extension-dicom-html@1.3.22) (2022-06-30)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-dicom-html
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [1.3.21](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-html@1.3.20...@ohif/extension-dicom-html@1.3.21) (2022-04-05)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-dicom-html
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [1.3.20](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-html@1.3.19...@ohif/extension-dicom-html@1.3.20) (2022-03-25)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-dicom-html
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [1.3.19](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-html@1.3.18...@ohif/extension-dicom-html@1.3.19) (2022-03-14)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-dicom-html
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [1.3.18](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-html@1.3.17...@ohif/extension-dicom-html@1.3.18) (2022-02-03)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-dicom-html
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [1.3.17](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-html@1.3.16...@ohif/extension-dicom-html@1.3.17) (2022-02-03)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-dicom-html
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [1.3.16](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-html@1.3.15...@ohif/extension-dicom-html@1.3.16) (2022-02-02)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-dicom-html
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [1.3.15](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-html@1.3.14...@ohif/extension-dicom-html@1.3.15) (2022-01-20)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-dicom-html
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [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)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-dicom-html
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [1.2.8](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-html@1.2.7...@ohif/extension-dicom-html@1.2.8) (2020-09-10)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-dicom-html
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [1.2.7](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-html@1.2.6...@ohif/extension-dicom-html@1.2.7) (2020-09-03)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-dicom-html
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [1.2.6](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-html@1.2.5...@ohif/extension-dicom-html@1.2.6) (2020-09-02)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-dicom-html
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [1.2.5](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-html@1.2.4...@ohif/extension-dicom-html@1.2.5) (2020-08-28)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-dicom-html
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [1.2.4](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-html@1.2.3...@ohif/extension-dicom-html@1.2.4) (2020-07-13)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-dicom-html
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [1.2.3](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-html@1.2.2...@ohif/extension-dicom-html@1.2.3) (2020-06-15)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* 🐛 Disable seg panel when data for seg unavailable ([#1732](https://github.com/OHIF/Viewers/issues/1732)) ([698e900](https://github.com/OHIF/Viewers/commit/698e900b85121d3c2a46747c443ef69fb7a8c95b)), closes [#1728](https://github.com/OHIF/Viewers/issues/1728)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [1.2.2](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-html@1.2.1...@ohif/extension-dicom-html@1.2.2) (2020-05-04)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* 🐛 Proper error handling for derived display sets ([#1708](https://github.com/OHIF/Viewers/issues/1708)) ([5b20d8f](https://github.com/OHIF/Viewers/commit/5b20d8f323e4b3ef9988f2f2ab672d697b6da409))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [1.2.1](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-html@1.2.0...@ohif/extension-dicom-html@1.2.1) (2020-04-28)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-dicom-html
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [1.2.0](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-html@1.1.2...@ohif/extension-dicom-html@1.2.0) (2020-04-24)
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@ohif/extension-dicom-html",
|
||||
"version": "1.2.0",
|
||||
"version": "1.3.24",
|
||||
"description": "OHIF extension for rendering structured reports to HTML",
|
||||
"author": "OHIF",
|
||||
"license": "MIT",
|
||||
@@ -29,7 +29,7 @@
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@ohif/core": "^0.50.0",
|
||||
"dcmjs": "^0.12.2",
|
||||
"dcmjs": "0.29.3",
|
||||
"prop-types": "^15.6.2",
|
||||
"react": "^16.8.6",
|
||||
"react-dom": "^16.8.6"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import * as dcmjs from 'dcmjs';
|
||||
import dcmjs from 'dcmjs';
|
||||
import TypedArrayProp from './TypedArrayProp';
|
||||
|
||||
import './DicomHtmlViewport.css';
|
||||
@@ -160,7 +160,7 @@ const getContentSequence = (data, level = 1) => {
|
||||
|
||||
const root = [];
|
||||
if (header) {
|
||||
const HeaderDynamicLevel = `h${level}`;
|
||||
const HeaderDynamicLevel = `h${Math.min(level, 6)}`;
|
||||
|
||||
root.push(<HeaderDynamicLevel key={header}>{header}</HeaderDynamicLevel>);
|
||||
}
|
||||
@@ -168,6 +168,16 @@ const getContentSequence = (data, level = 1) => {
|
||||
Object.keys(data).forEach(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;
|
||||
if (value instanceof Object) {
|
||||
content = getContentSequence(value, level + 1);
|
||||
@@ -207,7 +217,7 @@ class DicomHtmlViewport extends Component {
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
const dataSet = this.setContentFromByteArray(this.props.byteArray);
|
||||
this.setContentFromByteArray(this.props.byteArray);
|
||||
}
|
||||
|
||||
setContentFromByteArray(byteArray) {
|
||||
@@ -215,6 +225,7 @@ class DicomHtmlViewport extends Component {
|
||||
const dicomData = DicomMessage.readFile(arrayBuffer);
|
||||
const dataset = DicomMetaDictionary.naturalizeDataset(dicomData.dict);
|
||||
dataset._meta = DicomMetaDictionary.namifyDataset(dicomData.meta);
|
||||
dataset._meta = DicomMetaDictionary.naturalizeDataset(dataset._meta);
|
||||
|
||||
const mainData = getMainData(dataset);
|
||||
const contentSequence = getContentSequence(dataset);
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
import { MODULE_TYPES, utils } from '@ohif/core';
|
||||
import { MODULE_TYPES, utils, metadata } from '@ohif/core';
|
||||
|
||||
// TODO: Should probably use dcmjs for this
|
||||
const SOP_CLASS_UIDS = {
|
||||
BASIC_TEXT_SR: '1.2.840.10008.5.1.4.1.1.88.11',
|
||||
ENHANCED_SR: '1.2.840.10008.5.1.4.1.1.88.22',
|
||||
COMPREHENSIVE_SR: '1.2.840.10008.5.1.4.1.1.88.33',
|
||||
COMPREHENSIVE_3D_SR: '1.2.840.10008.5.1.4.1.1.88.34',
|
||||
PROCEDURE_LOG_STORAGE: '1.2.840.10008.5.1.4.1.1.88.40',
|
||||
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',
|
||||
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);
|
||||
@@ -22,7 +24,15 @@ const OHIFDicomHtmlSopClassHandler = {
|
||||
getDisplaySetFromSeries(series, study, dicomWebClient, authorizationHeaders) {
|
||||
const instance = series.getFirstInstance();
|
||||
|
||||
return {
|
||||
const metadata = instance.getData().metadata;
|
||||
const {
|
||||
SeriesDescription,
|
||||
SeriesNumber,
|
||||
SeriesDate,
|
||||
SeriesTime,
|
||||
} = metadata;
|
||||
|
||||
const srDisplaySet = {
|
||||
plugin: 'html',
|
||||
Modality: 'SR',
|
||||
displaySetInstanceUID: utils.guid(),
|
||||
@@ -31,8 +41,23 @@ const OHIFDicomHtmlSopClassHandler = {
|
||||
SOPInstanceUID: instance.getSOPInstanceUID(),
|
||||
SeriesInstanceUID: series.getSeriesInstanceUID(),
|
||||
StudyInstanceUID: study.getStudyInstanceUID(),
|
||||
SeriesDescription,
|
||||
metadata,
|
||||
SeriesDate,
|
||||
SeriesTime,
|
||||
SeriesNumber,
|
||||
authorizationHeaders,
|
||||
sopClassUids: sopClassUIDs,
|
||||
images: series._instances,
|
||||
isDerived: true,
|
||||
referencedDisplaySets: [],
|
||||
};
|
||||
|
||||
srDisplaySet.getSourceDisplaySet = function() {
|
||||
return srDisplaySet.referencedDisplaySets;
|
||||
};
|
||||
|
||||
return srDisplaySet;
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import React from 'react';
|
||||
import OHIFDicomHtmlSopClassHandler from './OHIFDicomHtmlSopClassHandler.js';
|
||||
import { version } from '../package.json';
|
||||
|
||||
const Component = React.lazy(() => {
|
||||
return import('./OHIFDicomHtmlViewport');
|
||||
@@ -18,6 +19,7 @@ export default {
|
||||
* Only required property. Should be a unique value across all extensions.
|
||||
*/
|
||||
id: 'html',
|
||||
version,
|
||||
|
||||
getViewportModule() {
|
||||
return OHIFDicomHtmlViewport;
|
||||
|
||||
@@ -3,6 +3,60 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
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)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-dicom-microscopy
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [0.51.2](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-microscopy@0.51.1...@ohif/extension-dicom-microscopy@0.51.2) (2020-09-03)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-dicom-microscopy
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [0.51.1](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-microscopy@0.51.0...@ohif/extension-dicom-microscopy@0.51.1) (2020-08-28)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-dicom-microscopy
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [0.51.0](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-microscopy@0.50.9...@ohif/extension-dicom-microscopy@0.51.0) (2020-06-04)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* 🎸 1729 - error boundary wrapper ([#1764](https://github.com/OHIF/Viewers/issues/1764)) ([c02b232](https://github.com/OHIF/Viewers/commit/c02b232b0cc24f38af5d5e3831d987d048e60ada))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [0.50.9](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-microscopy@0.50.8...@ohif/extension-dicom-microscopy@0.50.9) (2020-04-02)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-dicom-microscopy
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@ohif/extension-dicom-microscopy",
|
||||
"version": "0.50.9",
|
||||
"version": "0.52.0",
|
||||
"description": "OHIF extension for Dicom Microscopy",
|
||||
"author": "OHIF",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -12,6 +12,14 @@ const DicomMicroscopySopClassHandler = {
|
||||
getDisplaySetFromSeries(series, study, dicomWebClient) {
|
||||
const instance = series.getFirstInstance();
|
||||
|
||||
const metadata = instance.getData().metadata;
|
||||
const {
|
||||
SeriesDescription,
|
||||
SeriesNumber,
|
||||
ContentDate,
|
||||
ContentTime,
|
||||
} = metadata;
|
||||
|
||||
// Note: We are passing the dicomweb client into each viewport!
|
||||
|
||||
return {
|
||||
@@ -22,6 +30,11 @@ const DicomMicroscopySopClassHandler = {
|
||||
SOPInstanceUID: instance.getSOPInstanceUID(),
|
||||
SeriesInstanceUID: series.getSeriesInstanceUID(),
|
||||
StudyInstanceUID: study.getStudyInstanceUID(),
|
||||
SeriesDescription,
|
||||
SeriesDate: ContentDate, // Map ContentDate/Time to SeriesTime for series list sorting.
|
||||
SeriesTime: ContentTime,
|
||||
SeriesNumber,
|
||||
metadata,
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
@@ -62,11 +62,30 @@ class DicomMicroscopyViewport extends Component {
|
||||
);
|
||||
const microscopyViewer = api.VLWholeSlideMicroscopyImageViewer;
|
||||
|
||||
this.viewer = new microscopyViewer({
|
||||
client: dicomWebClient,
|
||||
metadata,
|
||||
retrieveRendered: false,
|
||||
});
|
||||
try {
|
||||
this.viewer = new microscopyViewer({
|
||||
client: dicomWebClient,
|
||||
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 });
|
||||
});
|
||||
|
||||
@@ -1,26 +1,26 @@
|
||||
import React from 'react';
|
||||
import DicomMicroscopySopClassHandler from './DicomMicroscopySopClassHandler.js';
|
||||
import { version } from '../package.json';
|
||||
|
||||
const Component = React.lazy(() => {
|
||||
return import('./DicomMicroscopyViewport');
|
||||
});
|
||||
|
||||
const DicomMicroscopyViewport = props => {
|
||||
return (
|
||||
<React.Suspense fallback={<div>Loading...</div>}>
|
||||
<Component {...props} />
|
||||
</React.Suspense>
|
||||
);
|
||||
};
|
||||
|
||||
export default {
|
||||
/**
|
||||
* Only required property. Should be a unique value across all extensions.
|
||||
*/
|
||||
id: 'microscopy',
|
||||
version,
|
||||
|
||||
getViewportModule() {
|
||||
return DicomMicroscopyViewport;
|
||||
getViewportModule({ servicesManager }) {
|
||||
return props => {
|
||||
return (
|
||||
<React.Suspense fallback={<div>Loading...</div>}>
|
||||
<Component {...props} servicesManager={servicesManager} />
|
||||
</React.Suspense>
|
||||
);
|
||||
};
|
||||
},
|
||||
getSopClassHandlerModule() {
|
||||
return DicomMicroscopySopClassHandler;
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
# Change Log
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [0.1.0](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-p10-downloader@0.0.2...@ohif/extension-dicom-p10-downloader@0.1.0) (2020-04-23)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* configuration to hook into XHR Error handling ([e96205d](https://github.com/OHIF/Viewers/commit/e96205de35e5bec14dc8a9a8509db3dd4e6ecdb6))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## 0.0.2 (2020-04-15)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-dicom-p10-downloader
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# Change Log
|
||||
|
||||
All notable changes to this project will be documented in this file. See
|
||||
[Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
@@ -1,43 +0,0 @@
|
||||
import { getDicomWebClientFromConfig } from './utils';
|
||||
import { getCommands } from './commandsModule';
|
||||
|
||||
/**
|
||||
* Constants
|
||||
*/
|
||||
|
||||
/**
|
||||
* Globals
|
||||
*/
|
||||
|
||||
const sharedContext = {
|
||||
dicomWebClient: null,
|
||||
};
|
||||
|
||||
/**
|
||||
* Extension
|
||||
*/
|
||||
export default {
|
||||
/**
|
||||
* Only required property. Should be a unique value across all extensions.
|
||||
*/
|
||||
id: 'dicom-p10-downloader',
|
||||
|
||||
/**
|
||||
* LIFECYCLE HOOKS
|
||||
*/
|
||||
|
||||
preRegistration({ appConfig }) {
|
||||
const dicomWebClient = getDicomWebClientFromConfig(appConfig);
|
||||
if (dicomWebClient) {
|
||||
sharedContext.dicomWebClient = dicomWebClient;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* MODULE GETTERS
|
||||
*/
|
||||
|
||||
getCommandsModule() {
|
||||
return getCommands(sharedContext);
|
||||
},
|
||||
};
|
||||
@@ -3,6 +3,57 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.1.2](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-pdf@1.1.1...@ohif/extension-dicom-pdf@1.1.2) (2022-01-29)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-dicom-pdf
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [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)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-dicom-pdf
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [1.0.5](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-pdf@1.0.4...@ohif/extension-dicom-pdf@1.0.5) (2020-09-03)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-dicom-pdf
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [1.0.4](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-pdf@1.0.3...@ohif/extension-dicom-pdf@1.0.4) (2020-08-28)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-dicom-pdf
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [1.0.3](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-pdf@1.0.2...@ohif/extension-dicom-pdf@1.0.3) (2020-04-02)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-dicom-pdf
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@ohif/extension-dicom-pdf",
|
||||
"version": "1.0.3",
|
||||
"version": "1.1.2",
|
||||
"description": "OHIF extension for Dicom PDF",
|
||||
"author": "OHIF",
|
||||
"license": "MIT",
|
||||
@@ -29,7 +29,7 @@
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@ohif/core": "^0.50.0",
|
||||
"dicom-parser": "^1.8.3",
|
||||
"dicom-parser": "^1.8.11",
|
||||
"prop-types": "^15.6.2",
|
||||
"react": "^16.8.6",
|
||||
"react-dom": "^16.8.6"
|
||||
|
||||
@@ -33,6 +33,7 @@ class DicomPDFViewport extends Component {
|
||||
|
||||
static propTypes = {
|
||||
byteArray: TypedArrayProp.uint8,
|
||||
rawPdf: PropTypes.bool,
|
||||
useNative: PropTypes.bool,
|
||||
viewportData: PropTypes.object,
|
||||
activeViewportIndex: PropTypes.number,
|
||||
@@ -45,7 +46,8 @@ class DicomPDFViewport extends Component {
|
||||
};
|
||||
|
||||
async componentDidMount() {
|
||||
const dataSet = this.parseByteArray(this.props.byteArray);
|
||||
const { rawPdf } = this.props
|
||||
const dataSet = !rawPdf && this.parseByteArray(this.props.byteArray);
|
||||
const fileURL = this.getPDFFileUrl(dataSet, this.props.byteArray);
|
||||
|
||||
this.setState(state => ({ ...state, fileURL }));
|
||||
|
||||
@@ -12,6 +12,14 @@ const OHIFDicomPDFSopClassHandler = {
|
||||
getDisplaySetFromSeries(series, study, dicomWebClient, authorizationHeaders) {
|
||||
const instance = series.getFirstInstance();
|
||||
|
||||
const metadata = instance.getData().metadata;
|
||||
const {
|
||||
ContentDate,
|
||||
ContentTime,
|
||||
SeriesDescription,
|
||||
SeriesNumber,
|
||||
} = metadata;
|
||||
|
||||
return {
|
||||
plugin: 'pdf',
|
||||
Modality: 'DOC',
|
||||
@@ -21,6 +29,11 @@ const OHIFDicomPDFSopClassHandler = {
|
||||
SOPInstanceUID: instance.getSOPInstanceUID(),
|
||||
SeriesInstanceUID: series.getSeriesInstanceUID(),
|
||||
StudyInstanceUID: study.getStudyInstanceUID(),
|
||||
SeriesDescription,
|
||||
SeriesDate: ContentDate, // Map ContentDate/Time to SeriesTime for series list sorting.
|
||||
SeriesTime: ContentTime,
|
||||
SeriesNumber,
|
||||
metadata,
|
||||
authorizationHeaders: authorizationHeaders,
|
||||
};
|
||||
},
|
||||
|
||||
@@ -3,6 +3,7 @@ import PropTypes from 'prop-types';
|
||||
import OHIF from '@ohif/core';
|
||||
import OHIFComponentPlugin from './OHIFComponentPlugin.js';
|
||||
import DicomPDFViewport from './DicomPDFViewport';
|
||||
import { str2ab } from '@ohif/core';
|
||||
|
||||
const { DicomLoaderService } = OHIF.utils;
|
||||
|
||||
@@ -18,6 +19,7 @@ class OHIFDicomPDFViewport extends Component {
|
||||
|
||||
state = {
|
||||
byteArray: null,
|
||||
rawPdf: false,
|
||||
error: null,
|
||||
};
|
||||
|
||||
@@ -34,6 +36,15 @@ class OHIFDicomPDFViewport extends Component {
|
||||
componentDidMount() {
|
||||
const { displaySet, studies } = this.props.viewportData;
|
||||
|
||||
if (displaySet.metadata && displaySet.metadata.EncapsulatedDocument) {
|
||||
const { InlineBinary, BulkDataURI } = displaySet.metadata.EncapsulatedDocument;
|
||||
if (InlineBinary) {
|
||||
const inlineBinaryData = atob(InlineBinary);
|
||||
const byteArray = str2ab(inlineBinaryData);
|
||||
this.setState({ byteArray, rawPdf: true });
|
||||
return;
|
||||
}
|
||||
}
|
||||
DicomLoaderService.findDicomDataPromise(displaySet, studies).then(
|
||||
data => this.setState({ byteArray: new Uint8Array(data) }),
|
||||
error => {
|
||||
@@ -49,15 +60,16 @@ class OHIFDicomPDFViewport extends Component {
|
||||
viewportIndex,
|
||||
activeViewportIndex,
|
||||
} = this.props;
|
||||
const { byteArray, error } = this.state;
|
||||
const { byteArray, error, rawPdf } = this.state;
|
||||
const { id, init, destroy } = OHIFDicomPDFViewport;
|
||||
const pluginProps = { id, init, destroy };
|
||||
|
||||
return (
|
||||
<OHIFComponentPlugin {...pluginProps}>
|
||||
{byteArray && (
|
||||
{(byteArray) && (
|
||||
<DicomPDFViewport
|
||||
byteArray={byteArray}
|
||||
rawPdf={rawPdf}
|
||||
setViewportActive={setViewportActive}
|
||||
viewportIndex={viewportIndex}
|
||||
activeViewportIndex={activeViewportIndex}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import React from 'react';
|
||||
import OHIFDicomPDFSopClassHandler from './OHIFDicomPDFSopClassHandler.js';
|
||||
import { version } from '../package.json';
|
||||
|
||||
const Component = React.lazy(() => {
|
||||
return import('./ConnectedOHIFDicomPDFViewer');
|
||||
@@ -18,6 +19,7 @@ export default {
|
||||
* Only required property. Should be a unique value across all extensions.
|
||||
*/
|
||||
id: 'pdf',
|
||||
version,
|
||||
getViewportModule() {
|
||||
return ConnectedOHIFDicomPDFViewer;
|
||||
},
|
||||
|
||||
@@ -3,6 +3,480 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [0.7.14](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-rt@0.7.13...@ohif/extension-dicom-rt@0.7.14) (2022-12-21)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-dicom-rt
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [0.7.13](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-rt@0.7.12...@ohif/extension-dicom-rt@0.7.13) (2022-09-01)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-dicom-rt
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [0.7.12](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-rt@0.7.11...@ohif/extension-dicom-rt@0.7.12) (2022-08-31)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-dicom-rt
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [0.7.11](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-rt@0.7.10...@ohif/extension-dicom-rt@0.7.11) (2022-04-05)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-dicom-rt
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [0.7.10](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-rt@0.7.9...@ohif/extension-dicom-rt@0.7.10) (2022-03-25)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-dicom-rt
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [0.7.9](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-rt@0.7.8...@ohif/extension-dicom-rt@0.7.9) (2022-03-14)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-dicom-rt
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [0.7.8](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-rt@0.7.7...@ohif/extension-dicom-rt@0.7.8) (2022-02-03)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-dicom-rt
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [0.7.7](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-rt@0.7.6...@ohif/extension-dicom-rt@0.7.7) (2022-02-02)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-dicom-rt
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [0.7.6](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-rt@0.7.5...@ohif/extension-dicom-rt@0.7.6) (2022-01-20)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-dicom-rt
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [0.7.5](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-rt@0.7.4...@ohif/extension-dicom-rt@0.7.5) (2022-01-19)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-dicom-rt
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [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)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-dicom-rt
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [0.4.8](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-rt@0.4.7...@ohif/extension-dicom-rt@0.4.8) (2020-10-07)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-dicom-rt
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [0.4.7](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-rt@0.4.6...@ohif/extension-dicom-rt@0.4.7) (2020-10-06)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-dicom-rt
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [0.4.6](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-rt@0.4.5...@ohif/extension-dicom-rt@0.4.6) (2020-09-17)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-dicom-rt
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [0.4.5](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-rt@0.4.4...@ohif/extension-dicom-rt@0.4.5) (2020-09-10)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-dicom-rt
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [0.4.4](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-rt@0.4.3...@ohif/extension-dicom-rt@0.4.4) (2020-09-10)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-dicom-rt
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [0.4.3](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-rt@0.4.2...@ohif/extension-dicom-rt@0.4.3) (2020-09-03)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-dicom-rt
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [0.4.2](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-rt@0.4.1...@ohif/extension-dicom-rt@0.4.2) (2020-09-02)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-dicom-rt
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [0.4.1](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-rt@0.4.0...@ohif/extension-dicom-rt@0.4.1) (2020-08-28)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-dicom-rt
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [0.4.0](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-rt@0.3.0...@ohif/extension-dicom-rt@0.4.0) (2020-08-18)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* 🎸 Update react-vtkjs-viewport usage to use requestPool ([#1984](https://github.com/OHIF/Viewers/issues/1984)) ([bb5f30c](https://github.com/OHIF/Viewers/commit/bb5f30ce2a0192d2e021beaaadfff22fd38e17b9))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [0.3.0](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-rt@0.2.7...@ohif/extension-dicom-rt@0.3.0) (2020-08-10)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* 🎸 Add support for POINT and OPEN_PLANAR for RT ([0e87ab3](https://github.com/OHIF/Viewers/commit/0e87ab37c29fcf9af74bbcefca854c6e6b8707bc))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [0.2.7](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-rt@0.2.6...@ohif/extension-dicom-rt@0.2.7) (2020-08-10)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-dicom-rt
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [0.2.6](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-rt@0.2.5...@ohif/extension-dicom-rt@0.2.6) (2020-08-05)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-dicom-rt
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [0.2.5](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-rt@0.2.4...@ohif/extension-dicom-rt@0.2.5) (2020-07-13)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* 🐛 - Put guards in all places that a cornerstone re-render ([#1899](https://github.com/OHIF/Viewers/issues/1899)) ([451f7ea](https://github.com/OHIF/Viewers/commit/451f7eab9258e7a193eb362e0926b13aedc4b3c9))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [0.2.4](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-rt@0.2.3...@ohif/extension-dicom-rt@0.2.4) (2020-07-13)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-dicom-rt
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [0.2.3](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-rt@0.2.2...@ohif/extension-dicom-rt@0.2.3) (2020-07-13)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-dicom-rt
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [0.2.2](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-rt@0.2.1...@ohif/extension-dicom-rt@0.2.2) (2020-07-13)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* 🐛 Fix RT Panel hide/show and Fix looping load errors ([#1877](https://github.com/OHIF/Viewers/issues/1877)) ([e7cc735](https://github.com/OHIF/Viewers/commit/e7cc735c03d02eeb0d3af4ba02c15ed4f81bbec2))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [0.2.1](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-rt@0.2.0...@ohif/extension-dicom-rt@0.2.1) (2020-06-15)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* 🐛 Disable seg panel when data for seg unavailable ([#1732](https://github.com/OHIF/Viewers/issues/1732)) ([698e900](https://github.com/OHIF/Viewers/commit/698e900b85121d3c2a46747c443ef69fb7a8c95b)), closes [#1728](https://github.com/OHIF/Viewers/issues/1728)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [0.2.0](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-rt@0.1.4...@ohif/extension-dicom-rt@0.2.0) (2020-06-04)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* 🎸 1729 - error boundary wrapper ([#1764](https://github.com/OHIF/Viewers/issues/1764)) ([c02b232](https://github.com/OHIF/Viewers/commit/c02b232b0cc24f38af5d5e3831d987d048e60ada))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [0.1.4](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-rt@0.1.3...@ohif/extension-dicom-rt@0.1.4) (2020-05-14)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* 🐛 Load default display set when no time metadata ([#1684](https://github.com/OHIF/Viewers/issues/1684)) ([f7b8b6a](https://github.com/OHIF/Viewers/commit/f7b8b6a41c4626084ef56b0fdf7363e914b143c4)), closes [#1683](https://github.com/OHIF/Viewers/issues/1683)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [0.1.3](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-rt@0.1.2...@ohif/extension-dicom-rt@0.1.3) (2020-05-12)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* 🐛 Fix seg color load ([#1724](https://github.com/OHIF/Viewers/issues/1724)) ([c4f84b1](https://github.com/OHIF/Viewers/commit/c4f84b1174d04ba84d37ed89b6d7ab541be28181))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [0.1.2](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-rt@0.1.1...@ohif/extension-dicom-rt@0.1.2) (2020-05-04)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* 🐛 Proper error handling for derived display sets ([#1708](https://github.com/OHIF/Viewers/issues/1708)) ([5b20d8f](https://github.com/OHIF/Viewers/commit/5b20d8f323e4b3ef9988f2f2ab672d697b6da409))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [0.1.1](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-rt@0.1.0...@ohif/extension-dicom-rt@0.1.1) (2020-04-28)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-dicom-rt
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [0.1.0](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-rt@0.0.2...@ohif/extension-dicom-rt@0.1.0) (2020-04-24)
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@ohif/extension-dicom-rt",
|
||||
"version": "0.1.0",
|
||||
"version": "0.7.14",
|
||||
"description": "OHIF extension for rendering DICOM RTSTRUCTs on top of cornerstone images.",
|
||||
"author": "OHIF",
|
||||
"license": "MIT",
|
||||
@@ -29,12 +29,14 @@
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@ohif/core": "^0.50.0",
|
||||
"cornerstone-core": "^2.2.8",
|
||||
"cornerstone-tools": "^4.0.9",
|
||||
"dcmjs": "^0.12.2",
|
||||
"cornerstone-core": "^2.6.1",
|
||||
"cornerstone-tools": "^6.0.6",
|
||||
"dcmjs": "0.29.3",
|
||||
"gl-matrix": "^3.3.0",
|
||||
"prop-types": "^15.6.2",
|
||||
"react": "^16.8.6",
|
||||
"react-dom": "^16.8.6"
|
||||
"react-dom": "^16.8.6",
|
||||
"react-tooltip": "4.2.10"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.5.5",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { MODULE_TYPES, utils, DICOMWeb } from '@ohif/core';
|
||||
import loadRTStruct from './loadRTStruct';
|
||||
import getSourceDisplaySet from './getSourceDisplaySet';
|
||||
|
||||
import id from './id';
|
||||
|
||||
@@ -26,6 +27,7 @@ const OHIFDicomRTStructSopClassHandler = {
|
||||
const {
|
||||
SeriesDate,
|
||||
SeriesTime,
|
||||
SeriesNumber,
|
||||
SeriesDescription,
|
||||
FrameOfReferenceUID,
|
||||
SOPInstanceUID,
|
||||
@@ -52,7 +54,9 @@ const OHIFDicomRTStructSopClassHandler = {
|
||||
isLoaded: false,
|
||||
SeriesDate,
|
||||
SeriesTime,
|
||||
SeriesNumber,
|
||||
SeriesDescription,
|
||||
metadata,
|
||||
};
|
||||
|
||||
if (!metadata.ReferencedSeriesSequence) {
|
||||
@@ -67,6 +71,10 @@ const OHIFDicomRTStructSopClassHandler = {
|
||||
}
|
||||
}
|
||||
|
||||
rtStructDisplaySet.getSourceDisplaySet = function (studies, activateLabelMap = true) {
|
||||
return getSourceDisplaySet(studies, rtStructDisplaySet, activateLabelMap);
|
||||
};
|
||||
|
||||
rtStructDisplaySet.load = function (referencedDisplaySet, studies) {
|
||||
return loadRTStruct(
|
||||
rtStructDisplaySet,
|
||||
@@ -74,6 +82,7 @@ const OHIFDicomRTStructSopClassHandler = {
|
||||
studies
|
||||
).catch(error => {
|
||||
rtStructDisplaySet.isLoaded = false;
|
||||
rtStructDisplaySet.loadError = true;
|
||||
throw new Error(error);
|
||||
});
|
||||
};
|
||||
|
||||
@@ -39,6 +39,8 @@
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.icons .angle-double-down, .icons .angle-double-up {
|
||||
.icons .angle-double-down,
|
||||
.icons .angle-double-up {
|
||||
margin-left: 15px;
|
||||
margin-right: -5px;
|
||||
}
|
||||
@@ -9,16 +9,21 @@
|
||||
|
||||
.dcmrt-panel .dcmrt-panel-header {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.dcmrt-panel .dcmrt-panel-header h3 {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.dcmrt-panel .dcmrt-panel-header .cog-icon {
|
||||
align-self: flex-end;
|
||||
color: var(--default-color);
|
||||
cursor: pointer;
|
||||
margin-bottom: 20px;
|
||||
min-height: 23px;
|
||||
max-height: 23px;
|
||||
}
|
||||
|
||||
.dcmrt-panel .dcmrt-panel-header .cog-icon:hover {
|
||||
|
||||
@@ -12,12 +12,17 @@ import StructureSetItem from '../StructureSetItem/StructureSetItem';
|
||||
import RTPanelSettings from '../RTSettings/RTSettings';
|
||||
import PanelSection from '../PanelSection/PanelSection';
|
||||
import LoadingIndicator from '../LoadingIndicator/LoadingIndicator';
|
||||
import TOOL_NAMES from '../../utils/toolNames';
|
||||
|
||||
const { RTSTRUCT_DISPLAY_TOOL } = TOOL_NAMES;
|
||||
|
||||
const { studyMetadataManager } = utils;
|
||||
|
||||
const refreshViewport = () => {
|
||||
cornerstone.getEnabledElements().forEach(enabledElement => {
|
||||
cornerstone.updateImage(enabledElement.element);
|
||||
if (enabledElement.image) {
|
||||
cornerstone.updateImage(enabledElement.element);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
@@ -26,23 +31,35 @@ const refreshViewport = () => {
|
||||
*
|
||||
* @param {Object} props
|
||||
* @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.isOpen - isOpen
|
||||
* @returns component
|
||||
*/
|
||||
const RTPanel = ({ studies, viewports, activeIndex, isOpen, onContourItemClick }) => {
|
||||
const RTPanel = ({
|
||||
studies,
|
||||
activeIndex,
|
||||
isOpen,
|
||||
onContourItemClick,
|
||||
activeContexts = [],
|
||||
contexts = {},
|
||||
activeViewport,
|
||||
getActiveViewport,
|
||||
}) => {
|
||||
const isVTK = () => activeContexts.includes(contexts.VTK);
|
||||
const isCornerstone = () => activeContexts.includes(contexts.CORNERSTONE);
|
||||
|
||||
const [selectedContour, setSelectedContour] = useState();
|
||||
const DEFAULT_SET_INDEX = 0;
|
||||
const DEFAULT_STATE = {
|
||||
referencedDisplaysets: [],
|
||||
sets: [],
|
||||
selectedSet: null,
|
||||
isLocked: false,
|
||||
};
|
||||
|
||||
const [state, setState] = useState(DEFAULT_STATE);
|
||||
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.
|
||||
@@ -62,25 +79,39 @@ const RTPanel = ({ studies, viewports, activeIndex, isOpen, onContourItemClick }
|
||||
* without subscribing to external events.
|
||||
*/
|
||||
useEffect(() => {
|
||||
document.addEventListener('extensiondicomrtrtloaded', updateStructureSets);
|
||||
updateStructureSets();
|
||||
|
||||
document.addEventListener('rt-panel-tab-updated', updateStructureSets);
|
||||
|
||||
return () => {
|
||||
document.removeEventListener('extensiondicomrtrtloaded', updateStructureSets);
|
||||
document.removeEventListener('rt-panel-tab-updated', updateStructureSets);
|
||||
};
|
||||
}, []);
|
||||
|
||||
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 viewport = getActiveViewport();
|
||||
const module = cornerstoneTools.getModule('rtstruct');
|
||||
const StructureSets = module.state.StructureSets;
|
||||
|
||||
if (StructureSets && StructureSets.length) {
|
||||
const viewportSets = module.getters.structuresSetsWhichReferenceSeriesInstanceUid(
|
||||
activeViewport.SeriesInstanceUID
|
||||
viewport.SeriesInstanceUID
|
||||
);
|
||||
|
||||
const studyMetadata = studyMetadataManager.get(activeViewport.StudyInstanceUID);
|
||||
const studyMetadata = studyMetadataManager.get(viewport.StudyInstanceUID);
|
||||
const referencedDisplaysets = studyMetadata.getDerivedDatasets({
|
||||
referencedSeriesInstanceUID: activeViewport.SeriesInstanceUID,
|
||||
referencedSeriesInstanceUID: viewport.SeriesInstanceUID,
|
||||
Modality: 'RTSTRUCT',
|
||||
});
|
||||
|
||||
@@ -89,7 +120,7 @@ const RTPanel = ({ studies, viewports, activeIndex, isOpen, onContourItemClick }
|
||||
setState({
|
||||
referencedDisplaysets,
|
||||
selectedSet: defaultSet,
|
||||
sets: viewportSets
|
||||
sets: viewportSets,
|
||||
});
|
||||
} else {
|
||||
setState(DEFAULT_STATE);
|
||||
@@ -97,15 +128,14 @@ const RTPanel = ({ studies, viewports, activeIndex, isOpen, onContourItemClick }
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
updateStructureSets();
|
||||
}, [studies, viewports, activeIndex]);
|
||||
|
||||
useEffect(() => {
|
||||
setShowSettings(showSettings && !isOpen);
|
||||
}, [isOpen]);
|
||||
|
||||
const toContourItem = ({ ROINumber, ROIName, RTROIObservations, colorArray, visible }, loadedSet) => {
|
||||
const toContourItem = (
|
||||
{ ROINumber, ROIName, RTROIObservations, colorArray, visible, isSupported },
|
||||
loadedSet
|
||||
) => {
|
||||
let interpretedType = '';
|
||||
if (RTROIObservations && RTROIObservations.RTROIInterpretedType) {
|
||||
interpretedType = `(${RTROIObservations.RTROIInterpretedType})`;
|
||||
@@ -116,36 +146,60 @@ const RTPanel = ({ studies, viewports, activeIndex, isOpen, onContourItemClick }
|
||||
<StructureSetItem
|
||||
key={ROINumber}
|
||||
selected={isSameContour}
|
||||
isDisabled={!isSupported}
|
||||
onClick={() => {
|
||||
setSelectedContour(isSameContour ? null : ROINumber);
|
||||
|
||||
const enabledElements = cornerstone.getEnabledElements();
|
||||
const element = enabledElements[activeIndex].element;
|
||||
const toolState = cornerstoneTools.getToolState(element, 'stack');
|
||||
if (isCornerstone()) {
|
||||
const enabledElements = cornerstone.getEnabledElements();
|
||||
const element = enabledElements[activeIndex].element;
|
||||
const stackToolState = cornerstoneTools.getToolState(
|
||||
element,
|
||||
'stack'
|
||||
);
|
||||
|
||||
if (!toolState) {
|
||||
return;
|
||||
if (!stackToolState) {
|
||||
return;
|
||||
}
|
||||
|
||||
const imageIds = stackToolState.data[0].imageIds;
|
||||
|
||||
const module = cornerstoneTools.getModule('rtstruct');
|
||||
const imageId = module.getters.imageIdOfCenterFrameOfROIContour(
|
||||
state.selectedSet.SeriesInstanceUID,
|
||||
ROINumber,
|
||||
imageIds
|
||||
);
|
||||
|
||||
const toolState = cornerstoneTools.globalImageIdSpecificToolStateManager.saveToolState();
|
||||
const imageIdSpecificToolState = toolState[imageId];
|
||||
|
||||
const rtstructData =
|
||||
imageIdSpecificToolState[RTSTRUCT_DISPLAY_TOOL];
|
||||
|
||||
const specificData = rtstructData.data.find(
|
||||
rtData => rtData.ROINumber === ROINumber
|
||||
);
|
||||
|
||||
specificData.highlight = true;
|
||||
|
||||
const frameIndex = imageIds.indexOf(imageId);
|
||||
const SOPInstanceUID = cornerstone.metaData.get(
|
||||
'SOPInstanceUID',
|
||||
imageId
|
||||
);
|
||||
const StudyInstanceUID = cornerstone.metaData.get(
|
||||
'StudyInstanceUID',
|
||||
imageId
|
||||
);
|
||||
|
||||
onContourItemClick({
|
||||
StudyInstanceUID,
|
||||
SOPInstanceUID,
|
||||
frameIndex,
|
||||
activeViewportIndex: activeIndex,
|
||||
});
|
||||
}
|
||||
|
||||
const imageIds = toolState.data[0].imageIds;
|
||||
|
||||
const module = cornerstoneTools.getModule('rtstruct');
|
||||
const imageId = module.getters.imageIdOfCenterFrameOfROIContour(
|
||||
state.selectedSet.SeriesInstanceUID,
|
||||
ROINumber,
|
||||
imageIds
|
||||
);
|
||||
|
||||
const frameIndex = imageIds.indexOf(imageId);
|
||||
const SOPInstanceUID = cornerstone.metaData.get('SOPInstanceUID', imageId);
|
||||
const StudyInstanceUID = cornerstone.metaData.get('StudyInstanceUID', imageId);
|
||||
|
||||
onContourItemClick({
|
||||
StudyInstanceUID,
|
||||
SOPInstanceUID,
|
||||
frameIndex,
|
||||
activeViewportIndex: activeIndex
|
||||
});
|
||||
}}
|
||||
label={`${ROIName} ${interpretedType}`}
|
||||
index={ROINumber}
|
||||
@@ -153,7 +207,10 @@ const RTPanel = ({ studies, viewports, activeIndex, isOpen, onContourItemClick }
|
||||
visible={visible}
|
||||
onVisibilityChange={() => {
|
||||
const module = cornerstoneTools.getModule('rtstruct');
|
||||
module.setters.toggleROIContour(state.selectedSet.SeriesInstanceUID, ROINumber);
|
||||
module.setters.toggleROIContour(
|
||||
state.selectedSet.SeriesInstanceUID,
|
||||
ROINumber
|
||||
);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
@@ -180,53 +237,85 @@ const RTPanel = ({ studies, viewports, activeIndex, isOpen, onContourItemClick }
|
||||
return (
|
||||
<div className="dcmrt-panel">
|
||||
<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>
|
||||
<Icon
|
||||
className="cog-icon"
|
||||
name="cog"
|
||||
width="25px"
|
||||
height="25px"
|
||||
onClick={() => setShowSettings(true)}
|
||||
/>
|
||||
</div>
|
||||
{!state.referencedDisplaysets.length && <LoadingIndicator expand height="70px" width="70px" />}
|
||||
{state.referencedDisplaysets.map(displaySet => {
|
||||
const { SeriesInstanceUID, metadata, isLoaded } = displaySet;
|
||||
{!state.isLocked && !state.referencedDisplaysets.length && (
|
||||
<LoadingIndicator expand height="70px" width="70px" />
|
||||
)}
|
||||
{state.sets &&
|
||||
state.referencedDisplaysets.map(displaySet => {
|
||||
const { SeriesInstanceUID, metadata, isLoaded } = displaySet;
|
||||
|
||||
const module = cornerstoneTools.getModule('rtstruct');
|
||||
const sets = module.getters.structuresSetsWhichReferenceSeriesInstanceUid(viewports[activeIndex].SeriesInstanceUID);
|
||||
const module = cornerstoneTools.getModule('rtstruct');
|
||||
const sets = module.getters.structuresSetsWhichReferenceSeriesInstanceUid(
|
||||
activeViewport.SeriesInstanceUID
|
||||
);
|
||||
|
||||
const loadedSet = sets.find(set => set.SeriesInstanceUID === SeriesInstanceUID);
|
||||
return (
|
||||
<PanelSection
|
||||
key={SeriesInstanceUID}
|
||||
title={metadata.StructureSetLabel}
|
||||
loading={!isLoaded || !loadedSet}
|
||||
visible={isLoaded && loadedSet.visible}
|
||||
hideVisibleButton={!isLoaded}
|
||||
expanded={isLoaded && loadedSet.SeriesInstanceUID === state.selectedSet.SeriesInstanceUID}
|
||||
onVisibilityChange={() => {
|
||||
const module = cornerstoneTools.getModule('rtstruct');
|
||||
module.setters.toggleStructureSet(SeriesInstanceUID);
|
||||
}}
|
||||
onExpandChange={async () => {
|
||||
if (!isLoaded) {
|
||||
await displaySet.load(viewports[activeIndex], studies);
|
||||
const module = cornerstoneTools.getModule('rtstruct');
|
||||
const sets = module.getters.structuresSetsWhichReferenceSeriesInstanceUid(viewports[activeIndex].SeriesInstanceUID);
|
||||
const selectedSet = sets.find(set => set.SeriesInstanceUID === SeriesInstanceUID);
|
||||
setState(state => ({ ...state, selectedSet, sets }));
|
||||
const loadedSet = sets.find(
|
||||
set => set.SeriesInstanceUID === SeriesInstanceUID
|
||||
);
|
||||
|
||||
if (!loadedSet) return null;
|
||||
|
||||
return (
|
||||
<PanelSection
|
||||
key={SeriesInstanceUID}
|
||||
title={metadata.StructureSetLabel}
|
||||
loading={!isLoaded || !loadedSet}
|
||||
visible={isLoaded && loadedSet && loadedSet.visible}
|
||||
hideVisibleButton={!isLoaded}
|
||||
expanded={
|
||||
isLoaded &&
|
||||
loadedSet.SeriesInstanceUID ===
|
||||
state.selectedSet.SeriesInstanceUID
|
||||
}
|
||||
}}
|
||||
>
|
||||
<ScrollableArea>
|
||||
<TableList headless>
|
||||
{isLoaded && loadedSet.ROIContours.map(c => toContourItem(c, loadedSet))}
|
||||
</TableList>
|
||||
</ScrollableArea>
|
||||
</PanelSection>
|
||||
);
|
||||
})}
|
||||
onVisibilityChange={newVisibility => {
|
||||
const module = cornerstoneTools.getModule('rtstruct');
|
||||
|
||||
if (newVisibility) {
|
||||
module.setters.showStructureSet(loadedSet.SeriesInstanceUID);
|
||||
} else {
|
||||
module.setters.hideStructureSet(loadedSet.SeriesInstanceUID);
|
||||
}
|
||||
const sets = module.getters.structuresSetsWhichReferenceSeriesInstanceUid(
|
||||
activeViewport.SeriesInstanceUID
|
||||
);
|
||||
setState(state => ({ ...state, sets }));
|
||||
refreshViewport();
|
||||
}}
|
||||
onExpandChange={async () => {
|
||||
if (!isLoaded) {
|
||||
await displaySet.load(activeViewport, studies);
|
||||
const module = cornerstoneTools.getModule('rtstruct');
|
||||
const sets = module.getters.structuresSetsWhichReferenceSeriesInstanceUid(
|
||||
activeViewport.SeriesInstanceUID
|
||||
);
|
||||
const selectedSet = sets.find(
|
||||
set => set.SeriesInstanceUID === SeriesInstanceUID
|
||||
);
|
||||
setState(state => ({ ...state, selectedSet, sets }));
|
||||
}
|
||||
}}
|
||||
>
|
||||
<ScrollableArea>
|
||||
<TableList headless>
|
||||
{isLoaded &&
|
||||
loadedSet.ROIContours.map(c => toContourItem(c, loadedSet))}
|
||||
</TableList>
|
||||
</ScrollableArea>
|
||||
</PanelSection>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -22,11 +22,6 @@
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.dcmrt-structure-set-item.selected .item-actions {
|
||||
height: 35px;
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
.dcmrt-structure-set-item .item-actions {
|
||||
margin-left: -1px;
|
||||
background-color: var(--ui-gray-darker);
|
||||
@@ -47,7 +42,8 @@
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.dcmrt-structure-set-item .item-actions .btnAction:hover, .dcmrt-structure-set-item .item-actions .btnAction:active {
|
||||
.dcmrt-structure-set-item .item-actions .btnAction:hover,
|
||||
.dcmrt-structure-set-item .item-actions .btnAction:active {
|
||||
color: var(--text-primary-color);
|
||||
}
|
||||
|
||||
@@ -86,3 +82,12 @@
|
||||
.dcmrt-structure-set-item .item-label .eye-icon.--visible {
|
||||
color: var(--default-color);
|
||||
}
|
||||
|
||||
.dcmrt-structure-set-item.isDisabled .item-color-section {
|
||||
background-color: #e29e4a;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.dcmrt-structure-set-item.isDisabled .item-label {
|
||||
color: var(--text-disabled-color);
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
import React, { useState } from 'react';
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { TableListItem, Icon } from '@ohif/ui';
|
||||
import { TableListItem, Icon, Tooltip, OverlayTrigger } from '@ohif/ui';
|
||||
import ReactTooltip from 'react-tooltip';
|
||||
|
||||
import './StructureSetItem.css';
|
||||
|
||||
@@ -20,6 +21,7 @@ ColoredCircle.propTypes = {
|
||||
const StructureSetItem = ({
|
||||
index,
|
||||
label,
|
||||
isDisabled,
|
||||
onClick,
|
||||
itemClass,
|
||||
color,
|
||||
@@ -28,20 +30,58 @@ const StructureSetItem = ({
|
||||
selected = false,
|
||||
}) => {
|
||||
const [isVisible, setIsVisible] = useState(visible);
|
||||
return (
|
||||
<div className={`dcmrt-structure-set-item ${selected && 'selected'}`}>
|
||||
<TableListItem
|
||||
key={index}
|
||||
itemKey={index}
|
||||
itemIndex={index}
|
||||
itemClass={itemClass}
|
||||
itemMeta={<ColoredCircle color={color} />}
|
||||
itemMetaClass="item-color-section"
|
||||
onItemClick={onClick}
|
||||
>
|
||||
<div>
|
||||
<div className="item-label" style={{ marginBottom: 4 }}>
|
||||
|
||||
useEffect(() => {
|
||||
setIsVisible(visible);
|
||||
}, [visible]);
|
||||
|
||||
let dcmrtClassNames = `dcmrt-structure-set-item`;
|
||||
|
||||
if (selected) {
|
||||
dcmrtClassNames += ' selected';
|
||||
}
|
||||
|
||||
if (isDisabled) {
|
||||
dcmrtClassNames += ' isDisabled';
|
||||
}
|
||||
|
||||
const warningIcon = (
|
||||
<span>
|
||||
<Icon name="exclamation-triangle" />
|
||||
</span>
|
||||
);
|
||||
|
||||
const tableListItem = (
|
||||
<TableListItem
|
||||
key={index}
|
||||
itemKey={index}
|
||||
itemIndex={index}
|
||||
itemClass={itemClass}
|
||||
itemMeta={isDisabled ? warningIcon : <ColoredCircle color={color} />}
|
||||
itemMetaClass="item-color-section"
|
||||
onItemClick={() => {
|
||||
if (isDisabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
onClick();
|
||||
}}
|
||||
>
|
||||
<div>
|
||||
<div className="item-label" style={{ marginBottom: 4 }}>
|
||||
<a data-tip data-for={`StructureHover${index}`}>
|
||||
<span>{label}</span>
|
||||
</a>
|
||||
<ReactTooltip
|
||||
id={`StructureHover${index}`}
|
||||
delayShow={250}
|
||||
place="right"
|
||||
border={true}
|
||||
type="light"
|
||||
>
|
||||
<span>{label}</span>
|
||||
</ReactTooltip>
|
||||
{!isDisabled && (
|
||||
<Icon
|
||||
className={`eye-icon ${isVisible && '--visible'}`}
|
||||
name={isVisible ? 'eye' : 'eye-closed'}
|
||||
@@ -49,37 +89,72 @@ const StructureSetItem = ({
|
||||
height="20px"
|
||||
onClick={event => {
|
||||
event.stopPropagation();
|
||||
|
||||
if (isDisabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
const newVisibility = !isVisible;
|
||||
setIsVisible(newVisibility);
|
||||
onVisibilityChange(newVisibility);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
{false && <div className="item-info">{'...'}</div>}
|
||||
{false && (
|
||||
<div className="item-actions">
|
||||
<button
|
||||
className="btnAction"
|
||||
onClick={() => console.log('Relabelling...')}
|
||||
>
|
||||
<span style={{ marginRight: '4px' }}>
|
||||
<Icon name="edit" width="14px" height="14px" />
|
||||
</span>
|
||||
Relabel
|
||||
</button>
|
||||
<button
|
||||
className="btnAction"
|
||||
onClick={() => console.log('Editing description...')}
|
||||
>
|
||||
<span style={{ marginRight: '4px' }}>
|
||||
<Icon name="edit" width="14px" height="14px" />
|
||||
</span>
|
||||
Description
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</TableListItem>
|
||||
|
||||
{false && <div className="item-info">{'...'}</div>}
|
||||
{false && (
|
||||
<div className="item-actions">
|
||||
<button
|
||||
className="btnAction"
|
||||
onClick={() => console.log('Relabelling...')}
|
||||
>
|
||||
<span style={{ marginRight: '4px' }}>
|
||||
<Icon name="edit" width="14px" height="14px" />
|
||||
</span>
|
||||
Relabel
|
||||
</button>
|
||||
<button
|
||||
className="btnAction"
|
||||
onClick={() => console.log('Editing description...')}
|
||||
>
|
||||
<span style={{ marginRight: '4px' }}>
|
||||
<Icon name="edit" width="14px" height="14px" />
|
||||
</span>
|
||||
Description
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</TableListItem>
|
||||
);
|
||||
|
||||
return (
|
||||
<div className={dcmrtClassNames}>
|
||||
<React.Fragment>
|
||||
{isDisabled ? (
|
||||
<OverlayTrigger
|
||||
key={index}
|
||||
placement="left"
|
||||
overlay={
|
||||
<Tooltip
|
||||
placement="left"
|
||||
className="in tooltip-warning"
|
||||
id="tooltip-left"
|
||||
>
|
||||
<div className="warningTitle">Unsupported Region</div>
|
||||
<div className="warningContent">
|
||||
Contour type currently unsupported.
|
||||
</div>
|
||||
</Tooltip>
|
||||
}
|
||||
>
|
||||
<div>{tableListItem}</div>
|
||||
</OverlayTrigger>
|
||||
) : (
|
||||
<React.Fragment>{tableListItem} </React.Fragment>
|
||||
)}
|
||||
</React.Fragment>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -94,7 +169,7 @@ StructureSetItem.propTypes = {
|
||||
|
||||
StructureSetItem.defaultProps = {
|
||||
itemClass: '',
|
||||
onClick: () => { },
|
||||
onClick: () => {},
|
||||
};
|
||||
|
||||
export default StructureSetItem;
|
||||
@@ -0,0 +1,14 @@
|
||||
import { metadata } from '@ohif/core';
|
||||
|
||||
export default function getSourceDisplaySet(studies, rtStructDisplaySet, activateLabelMap = true) {
|
||||
const referencedDisplaySet = metadata.StudyMetadata.getReferencedDisplaySet(
|
||||
rtStructDisplaySet,
|
||||
studies
|
||||
);
|
||||
|
||||
if (activateLabelMap) {
|
||||
rtStructDisplaySet.load(referencedDisplaySet, studies);
|
||||
}
|
||||
|
||||
return referencedDisplaySet;
|
||||
}
|
||||
@@ -1,15 +1,20 @@
|
||||
import React from 'react';
|
||||
import { utils } from '@ohif/core';
|
||||
|
||||
import init from './init.js';
|
||||
import sopClassHandlerModule from './OHIFDicomRTStructSopClassHandler';
|
||||
import id from './id.js';
|
||||
import RTPanel from './components/RTPanel/RTPanel';
|
||||
import { version } from '../package.json';
|
||||
|
||||
const { studyMetadataManager } = utils;
|
||||
|
||||
export default {
|
||||
/**
|
||||
* Only required property. Should be a unique value across all extensions.
|
||||
*/
|
||||
id,
|
||||
version,
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -20,41 +25,93 @@ export default {
|
||||
preRegistration({ servicesManager, configuration = {} }) {
|
||||
init({ servicesManager, configuration });
|
||||
},
|
||||
getPanelModule({ commandsManager }) {
|
||||
getPanelModule({ commandsManager, servicesManager, api }) {
|
||||
const ExtendedRTPanel = props => {
|
||||
const { activeContexts } = api.hooks.useAppContext();
|
||||
|
||||
const contourItemClickHandler = contourData => {
|
||||
commandsManager.runCommand('jumpToImage', contourData);
|
||||
};
|
||||
|
||||
return (
|
||||
<RTPanel {...props} onContourItemClick={contourItemClickHandler} />
|
||||
<RTPanel
|
||||
{...props}
|
||||
onContourItemClick={contourItemClickHandler}
|
||||
activeContexts={activeContexts}
|
||||
contexts={api.contexts}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
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 {
|
||||
menuOptions: [
|
||||
{
|
||||
icon: 'list',
|
||||
label: 'RTSTRUCT',
|
||||
target: 'rt-panel',
|
||||
isDisabled: studies => {
|
||||
stateEvent: RTPanelTabChangedEvent,
|
||||
isDisabled: (studies, activeViewport) => {
|
||||
if (!studies) {
|
||||
return true;
|
||||
}
|
||||
|
||||
for (let i = 0; i < studies.length; i++) {
|
||||
const study = studies[i];
|
||||
|
||||
if (study && study.series) {
|
||||
for (let j = 0; j < study.series.length; j++) {
|
||||
const series = study.series[j];
|
||||
if (
|
||||
/* Could be expanded to contain RTPLAN and RTDOSE information in the future */
|
||||
['RTSTRUCT'].includes(series.Modality)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (activeViewport) {
|
||||
const studyMetadata = studyMetadataManager.get(
|
||||
activeViewport.StudyInstanceUID
|
||||
);
|
||||
if (!studyMetadata) {
|
||||
return;
|
||||
}
|
||||
const referencedDisplaySets = studyMetadata.getDerivedDatasets({
|
||||
referencedSeriesInstanceUID: activeViewport.SeriesInstanceUID,
|
||||
Modality: 'RTSTRUCT',
|
||||
});
|
||||
if (
|
||||
referencedDisplaySets &&
|
||||
referencedDisplaySets.some(ds =>
|
||||
['RTSTRUCT'].includes(ds.Modality)
|
||||
)
|
||||
) {
|
||||
triggerRTPanelUpdatedEvent({
|
||||
badgeNumber: referencedDisplaySets.length,
|
||||
target: 'rt-panel',
|
||||
});
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import OHIF from '@ohif/core';
|
||||
import * as dcmjs from 'dcmjs';
|
||||
import dcmjs from 'dcmjs';
|
||||
import cornerstone from 'cornerstone-core';
|
||||
import cornerstoneTools from 'cornerstone-tools';
|
||||
import transformPointsToImagePlane from './utils/transformPointsToImagePlane';
|
||||
import TOOL_NAMES from './utils/toolNames';
|
||||
import { vec3 } from 'gl-matrix';
|
||||
|
||||
const dicomlab2RGB = dcmjs.data.Colors.dicomlab2RGB;
|
||||
const globalImageIdSpecificToolStateManager =
|
||||
@@ -16,7 +17,6 @@ export default async function loadRTStruct(
|
||||
studies
|
||||
) {
|
||||
const rtStructModule = cornerstoneTools.getModule('rtstruct');
|
||||
|
||||
// Set here is loading is asynchronous.
|
||||
// If this function throws its set back to false.
|
||||
rtStructDisplaySet.isLoaded = true;
|
||||
@@ -68,7 +68,6 @@ export default async function loadRTStruct(
|
||||
const rtStructDisplayToolName = TOOL_NAMES.RTSTRUCT_DISPLAY_TOOL;
|
||||
|
||||
for (let i = 0; i < ROIContourSequence.length; i++) {
|
||||
|
||||
const ROIContour = ROIContourSequence[i];
|
||||
const { ReferencedROINumber, ContourSequence } = ROIContour;
|
||||
|
||||
@@ -76,29 +75,32 @@ export default async function loadRTStruct(
|
||||
continue;
|
||||
}
|
||||
|
||||
_setROIContourMetadata(
|
||||
structureSet,
|
||||
StructureSetROISequence,
|
||||
RTROIObservationsSequence,
|
||||
ROIContour
|
||||
);
|
||||
const isSupported = false;
|
||||
|
||||
for (let c = 0; c < ContourSequence.length; c++) {
|
||||
const ContourSequenceArray = _toArray(ContourSequence);
|
||||
|
||||
for (let c = 0; c < ContourSequenceArray.length; c++) {
|
||||
const {
|
||||
ContourImageSequence,
|
||||
ContourData,
|
||||
NumberOfContourPoints,
|
||||
ContourGeometricType,
|
||||
} = ContourSequence[c];
|
||||
} = ContourSequenceArray[c];
|
||||
|
||||
if (ContourGeometricType !== 'CLOSED_PLANAR') {
|
||||
// TODO: Do we want to visualise types other than closed planar?
|
||||
// We could easily do open planar.
|
||||
const sopInstanceUID = ContourImageSequence
|
||||
? ContourImageSequence.ReferencedSOPInstanceUID
|
||||
: _getClosestSOPInstanceUID(
|
||||
ContourData,
|
||||
ContourGeometricType,
|
||||
NumberOfContourPoints,
|
||||
imageIdSopInstanceUidPairs
|
||||
);
|
||||
const imageId = _getImageId(imageIdSopInstanceUidPairs, sopInstanceUID);
|
||||
|
||||
if (!imageId) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const sopInstanceUID = ContourImageSequence.ReferencedSOPInstanceUID;
|
||||
const imageId = _getImageId(imageIdSopInstanceUidPairs, sopInstanceUID);
|
||||
const imageIdSpecificToolData = _getOrCreateImageIdSpecificToolData(
|
||||
toolState,
|
||||
imageId,
|
||||
@@ -107,27 +109,47 @@ export default async function loadRTStruct(
|
||||
|
||||
const imagePlane = cornerstone.metaData.get('imagePlaneModule', imageId);
|
||||
const points = [];
|
||||
let measurementData;
|
||||
|
||||
for (let p = 0; p < NumberOfContourPoints * 3; p += 3) {
|
||||
points.push({
|
||||
x: ContourData[p],
|
||||
y: ContourData[p + 1],
|
||||
z: ContourData[p + 2],
|
||||
});
|
||||
switch (ContourGeometricType) {
|
||||
case 'CLOSED_PLANAR':
|
||||
case 'OPEN_PLANAR':
|
||||
case 'POINT':
|
||||
isSupported = true;
|
||||
|
||||
for (let p = 0; p < NumberOfContourPoints * 3; p += 3) {
|
||||
points.push({
|
||||
x: ContourData[p],
|
||||
y: ContourData[p + 1],
|
||||
z: ContourData[p + 2],
|
||||
});
|
||||
}
|
||||
|
||||
transformPointsToImagePlane(points, imagePlane);
|
||||
|
||||
measurementData = {
|
||||
handles: {
|
||||
points,
|
||||
},
|
||||
type: ContourGeometricType,
|
||||
structureSetSeriesInstanceUid: rtStructDataset.SeriesInstanceUID,
|
||||
ROINumber: ReferencedROINumber,
|
||||
};
|
||||
|
||||
imageIdSpecificToolData.push(measurementData);
|
||||
break;
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
|
||||
transformPointsToImagePlane(points, imagePlane);
|
||||
|
||||
const measurementData = {
|
||||
handles: {
|
||||
points,
|
||||
},
|
||||
structureSetSeriesInstanceUid: rtStructDataset.SeriesInstanceUID,
|
||||
ROINumber: ReferencedROINumber,
|
||||
};
|
||||
|
||||
imageIdSpecificToolData.push(measurementData);
|
||||
}
|
||||
|
||||
_setROIContourMetadata(
|
||||
structureSet,
|
||||
StructureSetROISequence,
|
||||
RTROIObservationsSequence,
|
||||
ROIContour,
|
||||
isSupported
|
||||
);
|
||||
}
|
||||
|
||||
_setToolEnabledIfNotEnabled(rtStructDisplayToolName);
|
||||
@@ -149,7 +171,13 @@ export default async function loadRTStruct(
|
||||
* allows us to easily watch the module or the rtstruct loading process in any other component
|
||||
* without subscribing to external events.
|
||||
*/
|
||||
const event = new CustomEvent('extensiondicomrtrtloaded');
|
||||
const event = new CustomEvent('extensiondicomrtrtloaded', {
|
||||
detail: {
|
||||
rtStructDisplaySet,
|
||||
referencedDisplaySet,
|
||||
studies,
|
||||
},
|
||||
});
|
||||
document.dispatchEvent(event);
|
||||
}
|
||||
|
||||
@@ -157,7 +185,8 @@ function _setROIContourMetadata(
|
||||
structureSet,
|
||||
StructureSetROISequence,
|
||||
RTROIObservationsSequence,
|
||||
ROIContour
|
||||
ROIContour,
|
||||
isSupported
|
||||
) {
|
||||
const StructureSetROI = StructureSetROISequence.find(
|
||||
structureSetROI =>
|
||||
@@ -169,6 +198,7 @@ function _setROIContourMetadata(
|
||||
ROIName: StructureSetROI.ROIName,
|
||||
ROIGenerationAlgorithm: StructureSetROI.ROIGenerationAlgorithm,
|
||||
ROIDescription: StructureSetROI.ROIDescription,
|
||||
isSupported,
|
||||
visible: true,
|
||||
};
|
||||
|
||||
@@ -238,7 +268,7 @@ function _setROIContourRTROIObservations(
|
||||
|
||||
function _setToolEnabledIfNotEnabled(toolName) {
|
||||
cornerstone.getEnabledElements().forEach(enabledElement => {
|
||||
const { element } = enabledElement;
|
||||
const { element, image } = enabledElement;
|
||||
const tool = cornerstoneTools.getToolForElement(element, toolName);
|
||||
|
||||
if (tool.mode !== 'enabled') {
|
||||
@@ -246,7 +276,9 @@ function _setToolEnabledIfNotEnabled(toolName) {
|
||||
cornerstoneTools.setToolEnabled(toolName);
|
||||
}
|
||||
|
||||
cornerstone.updateImage(element);
|
||||
if (image) {
|
||||
cornerstone.updateImage(element);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -273,7 +305,9 @@ const _getImageId = (imageIdSopInstanceUidPairs, sopInstanceUID) => {
|
||||
imageIdSopInstanceUidPairsEntry.sopInstanceUID === sopInstanceUID
|
||||
);
|
||||
|
||||
return imageIdSopInstanceUidPairsEntry.imageId;
|
||||
return imageIdSopInstanceUidPairsEntry
|
||||
? imageIdSopInstanceUidPairsEntry.imageId
|
||||
: null;
|
||||
};
|
||||
|
||||
function _getImageIdSopInstanceUidPairsForDisplaySet(
|
||||
@@ -307,3 +341,82 @@ function _getImageIdSopInstanceUidPairsForDisplaySet(
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
function _toArray(objOrArray) {
|
||||
return Array.isArray(objOrArray) ? objOrArray : [objOrArray];
|
||||
}
|
||||
|
||||
function _getClosestSOPInstanceUID(
|
||||
ContourData,
|
||||
ContourGeometricType,
|
||||
NumberOfContourPoints,
|
||||
imageIdSopInstanceUidPairs
|
||||
) {
|
||||
const closest = {
|
||||
distance: Infinity,
|
||||
sopInstanceUID: null,
|
||||
};
|
||||
|
||||
let point;
|
||||
|
||||
switch (ContourGeometricType) {
|
||||
case 'POINT':
|
||||
point = ContourData;
|
||||
break;
|
||||
case 'CLOSED_PLANAR':
|
||||
case 'OPEN_PLANAR':
|
||||
// These are defined as planar, so get the of the region to get the
|
||||
// Best mapping to a plane even if its potentially off center.
|
||||
|
||||
point = [0, 0, 0];
|
||||
for (let p = 0; p < NumberOfContourPoints * 3; p += 3) {
|
||||
point[0] += ContourData[p];
|
||||
point[1] += ContourData[p + 1];
|
||||
point[2] += ContourData[p + 2];
|
||||
}
|
||||
|
||||
point[0] /= NumberOfContourPoints;
|
||||
point[1] /= NumberOfContourPoints;
|
||||
point[2] /= NumberOfContourPoints;
|
||||
}
|
||||
|
||||
imageIdSopInstanceUidPairs.forEach(pair => {
|
||||
const { imageId } = pair;
|
||||
|
||||
const imagePlaneModule = cornerstone.metaData.get(
|
||||
'imagePlaneModule',
|
||||
imageId
|
||||
);
|
||||
|
||||
const distance = distanceFromPointToPlane(point, imagePlaneModule);
|
||||
|
||||
if (distance < closest.distance) {
|
||||
closest.distance = distance;
|
||||
closest.sopInstanceUID = pair.sopInstanceUID;
|
||||
}
|
||||
});
|
||||
|
||||
return closest.sopInstanceUID;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {number[3]} P - The point
|
||||
* @param {object} imagePlaneModule The cornerstone metadata object for the imagePlane
|
||||
*/
|
||||
function distanceFromPointToPlane(P, imagePlaneModule) {
|
||||
const {
|
||||
rowCosines,
|
||||
columnCosines,
|
||||
imagePositionPatient: Q,
|
||||
} = imagePlaneModule;
|
||||
|
||||
let N = [];
|
||||
vec3.cross(N, rowCosines, columnCosines);
|
||||
|
||||
const [A, B, C] = N;
|
||||
|
||||
const D = -A * Q[0] - B * Q[1] - C * Q[2];
|
||||
|
||||
return Math.abs(A * P[0] + B * P[1] + C * P[2] + D); // Denominator is sqrt(A**2 + B**2 + C**2) which is 1 as its a normal vector
|
||||
}
|
||||
@@ -1,9 +1,10 @@
|
||||
import { importInternal, getToolState, toolColors } from 'cornerstone-tools';
|
||||
|
||||
import TOOL_NAMES from '../utils/toolNames';
|
||||
import drawCanvasCrosshairs from '../utils/drawCanvasCrosshairs';
|
||||
|
||||
// Cornerstone 3rd party dev kit imports
|
||||
const draw = importInternal('drawing/draw');
|
||||
const drawCircle = importInternal('drawing/drawCircle');
|
||||
const drawJoinedLines = importInternal('drawing/drawJoinedLines');
|
||||
const getNewContext = importInternal('drawing/getNewContext');
|
||||
const BaseTool = importInternal('base/BaseTool');
|
||||
@@ -36,11 +37,17 @@ export default class RTStructDisplayTool extends BaseTool {
|
||||
return;
|
||||
}
|
||||
|
||||
const { lineWidth, opacity } = rtstructModule.configuration;
|
||||
const {
|
||||
lineWidth,
|
||||
opacity,
|
||||
highlightOpacity,
|
||||
} = rtstructModule.configuration;
|
||||
|
||||
// We have tool data for this element - iterate over each one and draw it
|
||||
const context = getNewContext(eventData.canvasContext.canvas);
|
||||
|
||||
let crossHairCenter;
|
||||
|
||||
for (let i = 0; i < toolState.data.length; i++) {
|
||||
const data = toolState.data[i];
|
||||
|
||||
@@ -72,22 +79,76 @@ export default class RTStructDisplayTool extends BaseTool {
|
||||
const colorArray = ROIContourData.colorArray;
|
||||
const color = `rgba(${colorArray[0]},${colorArray[1]},${
|
||||
colorArray[2]
|
||||
},${opacity})`;
|
||||
},${opacity})`;
|
||||
|
||||
lineWidth;
|
||||
let highlight = data.highlight;
|
||||
const options = { color, lineWidth };
|
||||
|
||||
draw(context, context => {
|
||||
drawJoinedLines(
|
||||
context,
|
||||
eventData.element,
|
||||
points[points.length - 1],
|
||||
points,
|
||||
{
|
||||
color,
|
||||
lineWidth,
|
||||
}
|
||||
);
|
||||
if (highlight) {
|
||||
crossHairCenter = { x: 0, y: 0 };
|
||||
|
||||
points.forEach(point => {
|
||||
crossHairCenter.x += point.x;
|
||||
crossHairCenter.y += point.y;
|
||||
});
|
||||
|
||||
crossHairCenter.x /= points.length;
|
||||
crossHairCenter.y /= points.length;
|
||||
|
||||
// TODO: Disabling hightlight for now, it'd be good to bring it back
|
||||
// when we have a good way of doing this for SEG.
|
||||
|
||||
// options.fillStyle = color = `rgba(${colorArray[0]},${colorArray[1]},${
|
||||
// colorArray[2]
|
||||
// },${highlightOpacity})`;
|
||||
|
||||
// Draw highlight lines.
|
||||
|
||||
delete data.highlight; // Don't highlight on next render.
|
||||
}
|
||||
|
||||
switch (data.type) {
|
||||
case 'CLOSED_PLANAR':
|
||||
this._renderClosedPlanar(context, eventData.element, points, options);
|
||||
break;
|
||||
case 'POINT':
|
||||
this._renderPoint(context, eventData.element, points, options);
|
||||
break;
|
||||
case 'OPEN_PLANAR':
|
||||
this._renderOpenPlanar(context, eventData.element, points, options);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (crossHairCenter) {
|
||||
drawCanvasCrosshairs(eventData, crossHairCenter, {
|
||||
color: toolColors.getActiveColor(),
|
||||
lineWidth: 1,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
_renderClosedPlanar(context, element, points, options) {
|
||||
draw(context, context => {
|
||||
drawJoinedLines(
|
||||
context,
|
||||
element,
|
||||
points[points.length - 1],
|
||||
points,
|
||||
options
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
_renderPoint(context, element, points, options) {
|
||||
draw(context, context => {
|
||||
drawCircle(context, element, points[0], 3, options);
|
||||
});
|
||||
}
|
||||
|
||||
_renderOpenPlanar(context, element, points, options) {
|
||||
draw(context, context => {
|
||||
drawJoinedLines(context, element, points[0], points, options);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -5,13 +5,16 @@ import structureSetReferencesSeriesInstanceUid from './lib/structureSetReference
|
||||
// We should put this as a helper somewhere as we are using it in mutliple places.
|
||||
function refreshViewport() {
|
||||
cornerstone.getEnabledElements().forEach(enabledElement => {
|
||||
cornerstone.updateImage(enabledElement.element);
|
||||
if (enabledElement.image) {
|
||||
cornerstone.updateImage(enabledElement.element);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const configuration = {
|
||||
lineWidth: 3,
|
||||
opacity: 1.0,
|
||||
opacity: 0.75,
|
||||
highlightOpacity: 0.5,
|
||||
};
|
||||
|
||||
const state = {
|
||||
@@ -81,7 +84,9 @@ function _setStructureSetVisible(SeriesInstanceUID, visible = true) {
|
||||
const StructureSet = getStructureSet(SeriesInstanceUID);
|
||||
|
||||
if (StructureSet) {
|
||||
StructureSet.visible = visible;
|
||||
StructureSet.ROIContours.forEach(ROIContour => {
|
||||
ROIContour.visible = visible;
|
||||
});
|
||||
|
||||
refreshViewport();
|
||||
}
|
||||
@@ -154,15 +159,10 @@ function setToggleROIContour(SeriesInstanceUID, ROINumber) {
|
||||
* Returns an array of StructureSets which reference the given SeriesInstanceUID.
|
||||
* @param {string} SeriesInstanceUID The SeriesInstanceUID to check.
|
||||
*/
|
||||
function getStructuresSetsWhichReferenceSeriesInstanceUid(
|
||||
SeriesInstanceUID
|
||||
) {
|
||||
function getStructuresSetsWhichReferenceSeriesInstanceUid(SeriesInstanceUID) {
|
||||
const { StructureSets } = state;
|
||||
return StructureSets.filter(StructureSet =>
|
||||
structureSetReferencesSeriesInstanceUid(
|
||||
StructureSet,
|
||||
SeriesInstanceUID
|
||||
)
|
||||
structureSetReferencesSeriesInstanceUid(StructureSet, SeriesInstanceUID)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
import cornerstone from 'cornerstone-core';
|
||||
import cornerstoneTools from 'cornerstone-tools';
|
||||
|
||||
const { importInternal } = cornerstoneTools;
|
||||
const draw = importInternal('drawing/draw');
|
||||
const drawLine = importInternal('drawing/drawLine');
|
||||
const getNewContext = importInternal('drawing/getNewContext');
|
||||
|
||||
export default function _drawCanvasCrosshairs(eventData, center, options) {
|
||||
const context = getNewContext(eventData.canvasContext.canvas);
|
||||
const { element } = eventData;
|
||||
|
||||
const centerCanvas = cornerstone.pixelToCanvas(element, center);
|
||||
|
||||
const { clientWidth: width, clientHeight: height } = element;
|
||||
|
||||
const offset = 10;
|
||||
|
||||
draw(context, context => {
|
||||
drawLine(
|
||||
context,
|
||||
element,
|
||||
{ x: centerCanvas.x + offset, y: centerCanvas.y },
|
||||
{ x: width, y: centerCanvas.y },
|
||||
options,
|
||||
'canvas'
|
||||
);
|
||||
|
||||
drawLine(
|
||||
context,
|
||||
element,
|
||||
{ x: centerCanvas.x - offset, y: centerCanvas.y },
|
||||
{ x: 0, y: centerCanvas.y },
|
||||
options,
|
||||
'canvas'
|
||||
);
|
||||
|
||||
drawLine(
|
||||
context,
|
||||
element,
|
||||
{ x: centerCanvas.x, y: centerCanvas.y + offset },
|
||||
{ x: centerCanvas.x, y: height },
|
||||
options,
|
||||
'canvas'
|
||||
);
|
||||
|
||||
drawLine(
|
||||
context,
|
||||
element,
|
||||
{ x: centerCanvas.x, y: centerCanvas.y - offset },
|
||||
{ x: centerCanvas.x, y: 0 },
|
||||
options,
|
||||
'canvas'
|
||||
);
|
||||
});
|
||||
}
|
||||
@@ -3,6 +3,509 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [0.7.21](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-segmentation@0.7.20...@ohif/extension-dicom-segmentation@0.7.21) (2023-01-17)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-dicom-segmentation
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [0.7.20](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-segmentation@0.7.19...@ohif/extension-dicom-segmentation@0.7.20) (2023-01-10)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-dicom-segmentation
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [0.7.19](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-segmentation@0.7.18...@ohif/extension-dicom-segmentation@0.7.19) (2022-12-21)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-dicom-segmentation
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [0.7.18](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-segmentation@0.7.17...@ohif/extension-dicom-segmentation@0.7.18) (2022-12-21)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-dicom-segmentation
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [0.7.17](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-segmentation@0.7.16...@ohif/extension-dicom-segmentation@0.7.17) (2022-12-12)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* [#2964](https://github.com/OHIF/Viewers/issues/2964) Reword message for segmentation error loading due to orientation tolerance ([#3017](https://github.com/OHIF/Viewers/issues/3017)) ([597ac11](https://github.com/OHIF/Viewers/commit/597ac11c4daee1b1e14148804551e20611cfef08))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [0.7.16](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-segmentation@0.7.15...@ohif/extension-dicom-segmentation@0.7.16) (2022-10-28)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* [#2964](https://github.com/OHIF/Viewers/issues/2964) Update message for segmentation error loading due to orientation tolerance ([#2982](https://github.com/OHIF/Viewers/issues/2982)) ([545161d](https://github.com/OHIF/Viewers/commit/545161d5e8d92bb6652f17dc24a36dac8aa7b3a4))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [0.7.15](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-segmentation@0.7.14...@ohif/extension-dicom-segmentation@0.7.15) (2022-09-19)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-dicom-segmentation
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [0.7.14](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-segmentation@0.7.13...@ohif/extension-dicom-segmentation@0.7.14) (2022-09-12)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-dicom-segmentation
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [0.7.13](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-segmentation@0.7.12...@ohif/extension-dicom-segmentation@0.7.13) (2022-09-01)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-dicom-segmentation
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [0.7.12](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-segmentation@0.7.11...@ohif/extension-dicom-segmentation@0.7.12) (2022-08-31)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-dicom-segmentation
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [0.7.11](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-segmentation@0.7.10...@ohif/extension-dicom-segmentation@0.7.11) (2022-04-05)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-dicom-segmentation
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [0.7.10](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-segmentation@0.7.9...@ohif/extension-dicom-segmentation@0.7.10) (2022-03-25)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-dicom-segmentation
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [0.7.9](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-segmentation@0.7.8...@ohif/extension-dicom-segmentation@0.7.9) (2022-03-14)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-dicom-segmentation
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [0.7.8](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-segmentation@0.7.7...@ohif/extension-dicom-segmentation@0.7.8) (2022-02-03)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-dicom-segmentation
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [0.7.7](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-segmentation@0.7.6...@ohif/extension-dicom-segmentation@0.7.7) (2022-02-02)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-dicom-segmentation
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [0.7.6](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-segmentation@0.7.5...@ohif/extension-dicom-segmentation@0.7.6) (2022-01-20)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-dicom-segmentation
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [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)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-dicom-segmentation
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [0.4.6](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-segmentation@0.4.5...@ohif/extension-dicom-segmentation@0.4.6) (2020-09-17)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-dicom-segmentation
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [0.4.5](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-segmentation@0.4.4...@ohif/extension-dicom-segmentation@0.4.5) (2020-09-10)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-dicom-segmentation
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [0.4.4](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-segmentation@0.4.3...@ohif/extension-dicom-segmentation@0.4.4) (2020-09-10)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-dicom-segmentation
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [0.4.3](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-segmentation@0.4.2...@ohif/extension-dicom-segmentation@0.4.3) (2020-09-03)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-dicom-segmentation
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [0.4.2](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-segmentation@0.4.1...@ohif/extension-dicom-segmentation@0.4.2) (2020-09-02)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-dicom-segmentation
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [0.4.1](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-segmentation@0.4.0...@ohif/extension-dicom-segmentation@0.4.1) (2020-08-28)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-dicom-segmentation
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [0.4.0](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-segmentation@0.3.4...@ohif/extension-dicom-segmentation@0.4.0) (2020-08-18)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* 🎸 Update react-vtkjs-viewport usage to use requestPool ([#1984](https://github.com/OHIF/Viewers/issues/1984)) ([bb5f30c](https://github.com/OHIF/Viewers/commit/bb5f30ce2a0192d2e021beaaadfff22fd38e17b9))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [0.3.4](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-segmentation@0.3.3...@ohif/extension-dicom-segmentation@0.3.4) (2020-08-10)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-dicom-segmentation
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [0.3.3](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-segmentation@0.3.2...@ohif/extension-dicom-segmentation@0.3.3) (2020-07-13)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-dicom-segmentation
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [0.3.2](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-segmentation@0.3.1...@ohif/extension-dicom-segmentation@0.3.2) (2020-07-13)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* 🐛 Fix RT Panel hide/show and Fix looping load errors ([#1877](https://github.com/OHIF/Viewers/issues/1877)) ([e7cc735](https://github.com/OHIF/Viewers/commit/e7cc735c03d02eeb0d3af4ba02c15ed4f81bbec2))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [0.3.1](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-segmentation@0.3.0...@ohif/extension-dicom-segmentation@0.3.1) (2020-06-15)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* 🐛 Disable seg panel when data for seg unavailable ([#1732](https://github.com/OHIF/Viewers/issues/1732)) ([698e900](https://github.com/OHIF/Viewers/commit/698e900b85121d3c2a46747c443ef69fb7a8c95b)), closes [#1728](https://github.com/OHIF/Viewers/issues/1728)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [0.3.0](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-segmentation@0.2.6...@ohif/extension-dicom-segmentation@0.3.0) (2020-06-04)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* 🎸 1729 - error boundary wrapper ([#1764](https://github.com/OHIF/Viewers/issues/1764)) ([c02b232](https://github.com/OHIF/Viewers/commit/c02b232b0cc24f38af5d5e3831d987d048e60ada))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [0.2.6](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-segmentation@0.2.5...@ohif/extension-dicom-segmentation@0.2.6) (2020-05-12)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* 🐛 Fix seg color load ([#1724](https://github.com/OHIF/Viewers/issues/1724)) ([c4f84b1](https://github.com/OHIF/Viewers/commit/c4f84b1174d04ba84d37ed89b6d7ab541be28181))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [0.2.5](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-segmentation@0.2.4...@ohif/extension-dicom-segmentation@0.2.5) (2020-05-06)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-dicom-segmentation
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [0.2.4](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-segmentation@0.2.3...@ohif/extension-dicom-segmentation@0.2.4) (2020-05-04)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-dicom-segmentation
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [0.2.3](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-segmentation@0.2.2...@ohif/extension-dicom-segmentation@0.2.3) (2020-05-04)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* 🐛 Proper error handling for derived display sets ([#1708](https://github.com/OHIF/Viewers/issues/1708)) ([5b20d8f](https://github.com/OHIF/Viewers/commit/5b20d8f323e4b3ef9988f2f2ab672d697b6da409))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [0.2.2](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-segmentation@0.2.1...@ohif/extension-dicom-segmentation@0.2.2) (2020-04-28)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-dicom-segmentation
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [0.2.1](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-segmentation@0.2.0...@ohif/extension-dicom-segmentation@0.2.1) (2020-04-27)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* 🐛 Dropdown not updating when selecting segment ([#1675](https://github.com/OHIF/Viewers/issues/1675)) ([20e366f](https://github.com/OHIF/Viewers/commit/20e366ffbd76781f48dc99c9f59e24b255b5e5df)), closes [#1669](https://github.com/OHIF/Viewers/issues/1669)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [0.2.0](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-segmentation@0.1.2...@ohif/extension-dicom-segmentation@0.2.0) (2020-04-24)
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@ohif/extension-dicom-segmentation",
|
||||
"version": "0.2.0",
|
||||
"version": "0.7.21",
|
||||
"description": "OHIF extension for viewing segmentations in the 2D MPR view",
|
||||
"author": "OHIF",
|
||||
"license": "MIT",
|
||||
@@ -29,15 +29,17 @@
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@ohif/core": "^0.50.0",
|
||||
"cornerstone-core": "^2.2.8",
|
||||
"cornerstone-tools": "4.12.5",
|
||||
"dcmjs": "^0.12.2",
|
||||
"cornerstone-core": "^2.6.1",
|
||||
"cornerstone-tools": "^6.0.6",
|
||||
"dcmjs": "0.29.3",
|
||||
"prop-types": "^15.6.2",
|
||||
"react": "^16.8.6",
|
||||
"react-dom": "^16.8.6"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.5.5",
|
||||
"react-select": "^3.0.8"
|
||||
"gl-matrix": "^3.3.0",
|
||||
"react-select": "^3.0.8",
|
||||
"react-tooltip": "4.2.10"
|
||||
}
|
||||
}
|
||||
@@ -1,71 +0,0 @@
|
||||
import { MODULE_TYPES, utils } from '@ohif/core';
|
||||
import loadSegmentation from './loadSegmentation';
|
||||
|
||||
// TODO: Should probably use dcmjs for this
|
||||
const SOP_CLASS_UIDS = {
|
||||
DICOM_SEG: '1.2.840.10008.5.1.4.1.1.66.4',
|
||||
};
|
||||
|
||||
const sopClassUIDs = Object.values(SOP_CLASS_UIDS);
|
||||
|
||||
// TODO: Handle the case where there is more than one SOP Class Handler for the
|
||||
// same SOP Class.
|
||||
const OHIFDicomSegSopClassHandler = {
|
||||
id: 'OHIFDicomSegSopClassHandler',
|
||||
type: MODULE_TYPES.SOP_CLASS_HANDLER,
|
||||
sopClassUIDs,
|
||||
getDisplaySetFromSeries: function (
|
||||
series,
|
||||
study,
|
||||
dicomWebClient,
|
||||
authorizationHeaders
|
||||
) {
|
||||
const instance = series.getFirstInstance();
|
||||
const metadata = instance.getData().metadata;
|
||||
|
||||
const {
|
||||
SeriesDate,
|
||||
SeriesTime,
|
||||
SeriesDescription,
|
||||
FrameOfReferenceUID,
|
||||
SOPInstanceUID,
|
||||
SeriesInstanceUID,
|
||||
StudyInstanceUID,
|
||||
} = metadata;
|
||||
|
||||
const segDisplaySet = {
|
||||
Modality: 'SEG',
|
||||
displaySetInstanceUID: utils.guid(),
|
||||
wadoRoot: study.getData().wadoRoot,
|
||||
wadoUri: instance.getData().wadouri,
|
||||
SOPInstanceUID,
|
||||
SeriesInstanceUID,
|
||||
StudyInstanceUID,
|
||||
FrameOfReferenceUID,
|
||||
authorizationHeaders,
|
||||
metadata,
|
||||
isDerived: true,
|
||||
referencedDisplaySetUID: null, // Assigned when loaded.
|
||||
labelmapIndex: null, // Assigned when loaded.
|
||||
isLoaded: false,
|
||||
SeriesDate,
|
||||
SeriesTime,
|
||||
SeriesDescription,
|
||||
};
|
||||
|
||||
segDisplaySet.load = function (referencedDisplaySet, studies) {
|
||||
return loadSegmentation(
|
||||
segDisplaySet,
|
||||
referencedDisplaySet,
|
||||
studies
|
||||
).catch(error => {
|
||||
segDisplaySet.isLoaded = false;
|
||||
throw new Error(error);
|
||||
});
|
||||
};
|
||||
|
||||
return segDisplaySet;
|
||||
},
|
||||
};
|
||||
|
||||
export default OHIFDicomSegSopClassHandler;
|
||||
@@ -0,0 +1,100 @@
|
||||
import csTools from 'cornerstone-tools';
|
||||
import cs from 'cornerstone-core';
|
||||
import OHIF from '@ohif/core';
|
||||
|
||||
import DICOMSegTempCrosshairsTool from './tools/DICOMSegTempCrosshairsTool';
|
||||
import refreshViewports from './utils/refreshViewports';
|
||||
|
||||
const { studyMetadataManager } = OHIF.utils;
|
||||
|
||||
const commandsModule = ({ commandsManager }) => {
|
||||
const actions = {
|
||||
jumpToFirstSegment: ({ viewports }) => {
|
||||
try {
|
||||
const { activeViewportIndex, viewportSpecificData } = viewports;
|
||||
const viewport = viewportSpecificData[activeViewportIndex];
|
||||
const { StudyInstanceUID, displaySetInstanceUID } = viewport;
|
||||
const studyMetadata = studyMetadataManager.get(StudyInstanceUID);
|
||||
const firstImageId = studyMetadata.getFirstImageId(
|
||||
displaySetInstanceUID
|
||||
);
|
||||
|
||||
const module = csTools.getModule('segmentation');
|
||||
const brushStackState = module.state.series[firstImageId];
|
||||
const { labelmaps3D, activeLabelmapIndex } = brushStackState;
|
||||
const { labelmaps2D } = labelmaps3D[activeLabelmapIndex];
|
||||
|
||||
const firstLabelMap2D = labelmaps2D.find(value => !!value);
|
||||
const firstSegment = firstLabelMap2D.segmentsOnLabelmap[0];
|
||||
const segmentNumber = firstSegment;
|
||||
|
||||
const validIndexList = [];
|
||||
labelmaps2D.forEach((labelMap2D, index) => {
|
||||
if (labelMap2D.segmentsOnLabelmap.includes(segmentNumber)) {
|
||||
validIndexList.push(index);
|
||||
}
|
||||
});
|
||||
|
||||
const avg = array => array.reduce((a, b) => a + b) / array.length;
|
||||
const average = avg(validIndexList);
|
||||
const closest = validIndexList.reduce((prev, curr) => {
|
||||
return Math.abs(curr - average) < Math.abs(prev - average)
|
||||
? curr
|
||||
: prev;
|
||||
});
|
||||
|
||||
const enabledElements = cs.getEnabledElements();
|
||||
const element = enabledElements[activeViewportIndex].element;
|
||||
|
||||
const toolState = csTools.getToolState(element, 'stack');
|
||||
if (!toolState) return;
|
||||
|
||||
const imageIds = toolState.data[0].imageIds;
|
||||
const imageId = imageIds[closest];
|
||||
const frameIndex = imageIds.indexOf(imageId);
|
||||
const SOPInstanceUID = cs.metaData.get('SOPInstanceUID', imageId);
|
||||
|
||||
cs.getEnabledElements().forEach(enabledElement => {
|
||||
cs.updateImage(enabledElement.element);
|
||||
});
|
||||
|
||||
DICOMSegTempCrosshairsTool.addCrosshair(
|
||||
element,
|
||||
imageId,
|
||||
segmentNumber
|
||||
);
|
||||
|
||||
cs.getEnabledElements().forEach(enabledElement => {
|
||||
cs.updateImage(enabledElement.element);
|
||||
});
|
||||
|
||||
const refreshViewports = false;
|
||||
|
||||
commandsManager.runCommand('jumpToImage', {
|
||||
StudyInstanceUID,
|
||||
SOPInstanceUID,
|
||||
frameIndex,
|
||||
activeViewportIndex,
|
||||
refreshViewports,
|
||||
});
|
||||
} catch (error) {
|
||||
console.log('Error in moving to the first segment slice');
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
const definitions = {
|
||||
jumpToFirstSegment: {
|
||||
commandFn: actions.jumpToFirstSegment,
|
||||
storeContexts: ['viewports'],
|
||||
options: {},
|
||||
},
|
||||
};
|
||||
|
||||
return {
|
||||
definitions,
|
||||
defaultContext: 'VIEWER',
|
||||
};
|
||||
};
|
||||
|
||||
export default commandsModule;
|
||||
@@ -62,8 +62,8 @@
|
||||
}
|
||||
|
||||
.dcmseg-segment-item .segment-label span {
|
||||
overflow-wrap: normal;
|
||||
white-space: nowrap;
|
||||
overflow-wrap: break-word;
|
||||
white-space: normal;
|
||||
overflow: hidden;
|
||||
max-width: calc(100% - 40px); /* calc(100% - 50px); 20px = eye icon */
|
||||
text-overflow: ellipsis;
|
||||
@@ -71,6 +71,7 @@
|
||||
|
||||
.dcmseg-segment-item .segment-label .eye-icon {
|
||||
cursor: pointer;
|
||||
min-width: 20px;
|
||||
color: var(--active-color);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import React, { useState } from 'react';
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { TableListItem, Icon } from '@ohif/ui';
|
||||
import ReactTooltip from 'react-tooltip';
|
||||
|
||||
import './SegmentItem.css';
|
||||
|
||||
@@ -17,8 +18,31 @@ ColoredCircle.propTypes = {
|
||||
color: PropTypes.array.isRequired,
|
||||
};
|
||||
|
||||
const SegmentItem = ({ index, label, onClick, itemClass, color, visible = true, onVisibilityChange }) => {
|
||||
const SegmentItem = ({
|
||||
index,
|
||||
label,
|
||||
onClick,
|
||||
itemClass,
|
||||
color,
|
||||
labelmap3D,
|
||||
visible,
|
||||
onVisibilityChange,
|
||||
}) => {
|
||||
const [isVisible, setIsVisible] = useState(visible);
|
||||
|
||||
useEffect(() => {
|
||||
setIsVisible(visible);
|
||||
}, [visible]);
|
||||
|
||||
const onClickHandler = () => onClick(index);
|
||||
|
||||
const onVisibilityChangeHandler = event => {
|
||||
event.stopPropagation();
|
||||
const newVisibility = !isVisible;
|
||||
setIsVisible(newVisibility);
|
||||
onVisibilityChange(newVisibility, index, labelmap3D);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="dcmseg-segment-item">
|
||||
<TableListItem
|
||||
@@ -28,22 +52,28 @@ const SegmentItem = ({ index, label, onClick, itemClass, color, visible = true,
|
||||
itemClass={itemClass}
|
||||
itemMeta={<ColoredCircle color={color} />}
|
||||
itemMetaClass="segment-color-section"
|
||||
onItemClick={onClick}
|
||||
onItemClick={onClickHandler}
|
||||
>
|
||||
<div>
|
||||
<div className="segment-label" style={{ marginBottom: 4 }}>
|
||||
<span>{label}</span>
|
||||
<a data-tip data-for={`SegmentHover${index}`}>
|
||||
<span>{label}</span>
|
||||
</a>
|
||||
<ReactTooltip
|
||||
id={`SegmentHover${index}`}
|
||||
delayShow={250}
|
||||
place="right"
|
||||
border={true}
|
||||
type="light"
|
||||
>
|
||||
<span>{label}</span>
|
||||
</ReactTooltip>
|
||||
<Icon
|
||||
className={`eye-icon ${isVisible && '--visible'}`}
|
||||
name={isVisible ? 'eye' : 'eye-closed'}
|
||||
width="20px"
|
||||
height="20px"
|
||||
onClick={event => {
|
||||
event.stopPropagation();
|
||||
const newVisibility = !isVisible;
|
||||
setIsVisible(newVisibility);
|
||||
onVisibilityChange(newVisibility);
|
||||
}}
|
||||
onClick={onVisibilityChangeHandler}
|
||||
/>
|
||||
</div>
|
||||
{false && <div className="segment-info">{'...'}</div>}
|
||||
@@ -56,7 +86,7 @@ const SegmentItem = ({ index, label, onClick, itemClass, color, visible = true,
|
||||
<span style={{ marginRight: '4px' }}>
|
||||
<Icon name="edit" width="14px" height="14px" />
|
||||
</span>
|
||||
Relabel
|
||||
Relabel
|
||||
</button>
|
||||
<button
|
||||
className="btnAction"
|
||||
@@ -65,7 +95,7 @@ const SegmentItem = ({ index, label, onClick, itemClass, color, visible = true,
|
||||
<span style={{ marginRight: '4px' }}>
|
||||
<Icon name="edit" width="14px" height="14px" />
|
||||
</span>
|
||||
Description
|
||||
Description
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
@@ -85,7 +115,7 @@ SegmentItem.propTypes = {
|
||||
|
||||
SegmentItem.defaultProps = {
|
||||
itemClass: '',
|
||||
onClick: () => { }
|
||||
onClick: () => {},
|
||||
};
|
||||
|
||||
export default SegmentItem;
|
||||
+69
-5
@@ -6,22 +6,28 @@
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.dcmseg-segmentation-panel.disabled {
|
||||
opacity: 0.6;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.dcmseg-segmentation-panel h3 {
|
||||
padding-left: 20px;
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.dcmseg-segmentation-panel .segmentations {
|
||||
padding: 0 20px 20px 20px;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
.dcmseg-segmentation-panel .cog-icon {
|
||||
margin: 20px 20px 0 0;
|
||||
align-self: flex-end;
|
||||
color: var(--default-color);
|
||||
cursor: pointer;
|
||||
min-height: 23px;
|
||||
max-height: 23px;
|
||||
}
|
||||
|
||||
.dcmseg-segmentation-panel .cog-icon:hover {
|
||||
@@ -43,11 +49,11 @@
|
||||
height: 150px;
|
||||
}
|
||||
|
||||
.dcmseg-segmentation-panel .selector-form>div {
|
||||
.dcmseg-segmentation-panel .selector-form > div {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.dcmseg-segmentation-panel .selector-form>div:first-of-type {
|
||||
.dcmseg-segmentation-panel .selector-form > div:first-of-type {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
@@ -59,3 +65,61 @@
|
||||
flex: unset;
|
||||
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;
|
||||
}
|
||||
+588
-336
File diff suppressed because it is too large.
Load diff
Loaded 100 of 536 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user