Compare commits

...
Author SHA1 Message Date
ohif-bot 58d38495f0 chore(version): Update package versions [skip ci] 2023-07-27 16:47:28 +00:00
ohif-bot 862f255be2 chore(version): version.json [skip ci] 2023-07-27 16:47:24 +00:00
Alireza 72207e6c78 dev(yarn link): make it possible to yarn link easily with cornerstone3D and update docs (#3563) 2023-07-27 12:39:12 -04:00
ohif-bot 6d3b3d191f chore(version): Update package versions [skip ci] 2023-07-27 13:59:21 +00:00
ohif-bot d8867d3804 chore(version): version.json [skip ci] 2023-07-27 13:59:17 +00:00
Andres Diaz-Pinto 019dedce34 docs(readme): fix broken DICOMWeb link in main Readme (#3561)
Signed-off-by: Andres <diazandr3s@gmail.com>
2023-07-27 09:51:29 -04:00
ohif-bot 4d104add05 chore(version): Update package versions [skip ci] 2023-07-26 15:42:03 +00:00
ohif-bot 924a5478f3 chore(version): version.json [skip ci] 2023-07-26 15:41:59 +00:00
Alireza d8f628b125 fix(rerendering): reduce the re-rendering of the viewport grid (#3558) 2023-07-26 11:34:50 -04:00
ohif-bot cab8d322c2 chore(version): Update package versions [skip ci] 2023-07-21 12:34:48 +00:00
ohif-bot 3ef26cc670 chore(version): version.json [skip ci] 2023-07-21 12:34:45 +00:00
Wenqi Li 1efe2db662 docs: fixes a README.md typo (#3553) 2023-07-21 08:27:51 -04:00
ohif-bot 72933af984 chore(version): Update package versions [skip ci] 2023-07-20 16:17:02 +00:00
ohif-bot 2645e3c7aa chore(version): version.json [skip ci] 2023-07-20 16:16:58 +00:00
Alireza 81ab52784a fix(oauth): handle no user scenario (#3556) 2023-07-20 12:08:54 -04:00
ohif-bot 09a302250c chore(version): Update package versions [skip ci] 2023-07-20 15:50:41 +00:00
ohif-bot 783bab7c9d chore(version): version.json [skip ci] 2023-07-20 15:50:38 +00:00
Joe Boccanfuso 7bbc213f58 feat(DataSource): Allow for dynamic data source configuration and creation (#3543) 2023-07-20 11:43:05 -04:00
ohif-bot 6d88978710 chore(version): Update package versions [skip ci] 2023-07-19 19:28:14 +00:00
ohif-bot 32bb553b05 chore(version): version.json [skip ci] 2023-07-19 19:28:11 +00:00
Bill Wallace d5cbbb037d Fix the wrong aspect ratio load (#3534) 2023-07-19 15:21:19 -04:00
ohif-bot b904ea0f23 chore(version): Update package versions [skip ci] 2023-07-19 13:49:16 +00:00
ohif-bot 839923e578 chore(version): version.json [skip ci] 2023-07-19 13:49:13 +00:00
Bill Wallace 030e1e4104 chore(test): Integration test wait until rendered/queried instead of timed (#3540) 2023-07-19 09:41:47 -04:00
ohif-bot 265c1fbb10 chore(version): Update package versions [skip ci] 2023-07-13 13:13:19 +00:00
ohif-bot f3aa8a8b61 chore(version): version.json [skip ci] 2023-07-13 13:13:14 +00:00
Joe Boccanfuso 18156bb7b1 fix(PT measurement units): Use the modality unit that arrives from cornerstone3d instead of calculating the modality unit (again) here. (#3533) 2023-07-13 09:03:17 -04:00
ohif-bot 93a009f1a6 chore(version): Update package versions [skip ci] 2023-07-12 14:26:38 +00:00
ohif-bot 11471e6fc1 chore(version): version.json [skip ci] 2023-07-12 14:26:35 +00:00
Joe Boccanfuso 7d286a07f2 fix(Worklist): use the flexbox gap style property for spacing between mode buttons (#3532) 2023-07-12 10:19:03 -04:00
ohif-bot 19418df8b5 chore(version): Update package versions [skip ci] 2023-07-12 12:35:35 +00:00
ohif-bot c9eb0250dd chore(version): version.json [skip ci] 2023-07-12 12:35:32 +00:00
Joe Boccanfuso c3cd265430 fix(Measurement tracking): Side panel study browser can now be closed and measurements are still tracked (#3530) 2023-07-12 08:28:48 -04:00
ohif-bot 913264d289 chore(version): Update package versions [skip ci] 2023-07-07 16:44:37 +00:00
ohif-bot 74acc02c37 chore(version): version.json [skip ci] 2023-07-07 16:44:34 +00:00
m00n620 453ff0be4f fix(cli): extension template (#3512) 2023-07-07 12:37:36 -04:00
ohif-bot 2e040d550d chore(version): Update package versions [skip ci] 2023-07-07 16:01:21 +00:00
ohif-bot bfa1b1c7c0 chore(version): version.json [skip ci] 2023-07-07 16:01:17 +00:00
Sofien-Sellami a3e7ff479a fix(tmtv): jumpToClick Tool for TMTV mode (#3525) 2023-07-07 11:53:54 -04:00
ohif-bot 87f361516c chore(version): Update package versions [skip ci] 2023-07-07 15:37:42 +00:00
ohif-bot 5eaf6168f6 chore(version): version.json [skip ci] 2023-07-07 15:37:38 +00:00
Sofien-Sellami 5ab74f5020 ui(InputNumber): adding label element in InputNumber Component and … (#3501) 2023-07-07 11:30:52 -04:00
ohif-bot 92273b25b3 chore(version): Update package versions [skip ci] 2023-07-06 16:23:25 +00:00
ohif-bot 3f296bdeb5 chore(version): version.json [skip ci] 2023-07-06 16:23:22 +00:00
Attila Hornok 0600858055 docs(ohif): broken link in the docs (#3509) 2023-07-06 12:16:09 -04:00
Alireza 4a184d0be7 fix(invert): allows volume viewport to invert and sync their invert status (#3514) 2023-07-06 12:09:34 -04:00
ohif-bot f3a4f861b4 chore(version): Update package versions [skip ci] 2023-07-05 13:11:37 +00:00
ohif-bot fc19912162 chore(version): version.json [skip ci] 2023-07-05 13:11:33 +00:00
Joe Boccanfuso 61595ce7d6 feat(Docker): Facilitate the ability to run the OHIF docker image over SSL (#3511) 2023-07-05 09:03:43 -04:00
ohif-bot ffd53eff85 chore(version): Update package versions [skip ci] 2023-07-05 12:50:14 +00:00
ohif-bot 52a43c771a chore(version): version.json [skip ci] 2023-07-05 12:50:10 +00:00
Joe Boccanfuso c3c132d268 fix(tracked-measurement): SR viewports disable jump to measurement (#3518) 2023-07-05 08:43:01 -04:00
ohif-bot e742328ac1 chore(version): Update package versions [skip ci] 2023-07-04 18:46:19 +00:00
ohif-bot 90647b63a0 chore(version): version.json [skip ci] 2023-07-04 18:46:16 +00:00
Joe Boccanfuso ed44ad7f4b fix(segmentation): Use the series description for the segmentation label (#3517) 2023-07-04 14:39:39 -04:00
ohif-bot b0a456a46c chore(version): Update package versions [skip ci] 2023-07-04 16:41:18 +00:00
ohif-bot 8b7db45641 chore(version): version.json [skip ci] 2023-07-04 16:41:14 +00:00
Joe Boccanfuso b5599dde4e fix(utils): remove undefined/null values from the PerFrameFunctionalGroupsSequence in combineFrameInstance. (#3515) 2023-07-04 12:33:51 -04:00
ohif-bot f7b7131d70 chore(version): Update package versions [skip ci] 2023-07-04 16:05:36 +00:00
ohif-bot d646c15c27 chore(version): version.json [skip ci] 2023-07-04 16:05:32 +00:00
Bill WallaceandAlireza 64f8bb580a fix(colormap): update opacityMapping to opacity and use simple list renderer(#3464)
Co-authored-by: Alireza <ar.sedghi@gmail.com>
2023-07-04 11:57:59 -04:00
ohif-bot ce29bbe795 chore(version): Update package versions [skip ci] 2023-07-04 13:04:17 +00:00
ohif-bot 0bbcb4269d chore(version): version.json [skip ci] 2023-07-04 13:04:14 +00:00
Alireza 3efa793e60 fix(utils): remove undefined values in combineFrameInstance (#3513) 2023-07-04 08:57:14 -04:00
ohif-bot 3b50bd8cc4 chore(version): Update package versions [skip ci] 2023-07-03 14:41:24 +00:00
ohif-bot cd553e29b2 chore(version): version.json [skip ci] 2023-07-03 14:41:21 +00:00
mccle 12dd5a2619 fix(typeError): Address SliceThickness.toFixed TypeError (#3510) 2023-07-03 10:31:46 -04:00
ohif-bot f7a435c2f3 chore(version): Update package versions [skip ci] 2023-06-29 19:56:51 +00:00
ohif-bot 19f1da702f chore(version): version.json [skip ci] 2023-06-29 19:56:47 +00:00
Sofien-Sellami 14974b6973 fix(tmtv): Fusion_Viewport to allow displaySets to be changed (#3502)
Allows drag and drop of new sets of viewports to change them one at a time.
Co-authored-by: Bill Wallace <wayfarer3130@gmail.com>
2023-06-29 15:48:52 -04:00
ohif-bot b4dbe5656b chore(version): Update package versions [skip ci] 2023-06-29 15:24:35 +00:00
ohif-bot c6f7349fd7 chore(version): version.json [skip ci] 2023-06-29 15:24:32 +00:00
Sofien-Sellami abe939bd20 fix(hp): Remove filtered displaySets from parameter from study protocolEngine.findMatch() (#3343)
The changes look good and are running now successfully.
2023-06-29 11:17:08 -04:00
ohif-bot 4d4dd45c23 chore(version): Update package versions [skip ci] 2023-06-29 15:04:16 +00:00
ohif-bot 7671b9faf4 chore(version): version.json [skip ci] 2023-06-29 15:04:12 +00:00
Sofien-SellamiandBill Wallace e653961858 Add support for array input for all constraints in HangingProtocolService validator (#3322)
Co-authored-by: Bill Wallace <wayfarer3130@gmail.com>
2023-06-29 10:56:40 -04:00
ohif-bot aeb61f40da chore(version): Update package versions [skip ci] 2023-06-29 14:51:44 +00:00
ohif-bot b5fb2461dd chore(version): version.json [skip ci] 2023-06-29 14:51:41 +00:00
Joe Boccanfuso 15ad8f29cd fix(date formatting): Show study date as a fallback for an empty series date and show 'No Study Date' as a back up for both (#3483) 2023-06-29 10:43:56 -04:00
ohif-bot fa37593696 chore(version): Update package versions [skip ci] 2023-06-29 14:38:20 +00:00
ohif-bot 08eed4cdf9 chore(version): version.json [skip ci] 2023-06-29 14:38:15 +00:00
Sofien-Sellami b5b7d323c8 fix: allow volume3d viewport for SUV rendering (#3494)
Add checks to include volume3d viewports.
2023-06-29 10:30:41 -04:00
ohif-bot 8a8ae237d2 chore(version): Update package versions [skip ci] 2023-06-27 15:52:28 +00:00
ohif-bot cf1e00fee0 chore(version): version.json [skip ci] 2023-06-27 15:52:24 +00:00
Joe Boccanfuso ef4f28690a fix(DICOM Upload): use correct Accept header for upload (#3498) 2023-06-27 11:44:46 -04:00
ohif-bot c98211ccb5 chore(version): Update package versions [skip ci] 2023-06-23 00:55:55 +00:00
ohif-bot 71342fac8f chore(version): version.json [skip ci] 2023-06-23 00:55:52 +00:00
Alireza 52f419d188 feat(transferSyntax): Add customizable Transfer syntax through datasource config (#3406) 2023-06-22 20:47:50 -04:00
Salim Kanoun ca3a0b56f7 update old configs 2023-06-22 21:26:26 +02:00
Salim Kanoun 1a91f210ab rename 2023-06-20 22:18:14 +02:00
ohif-bot 3503e6e9f4 chore(version): Update package versions [skip ci] 2023-06-20 14:33:52 +00:00
ohif-bot 0c51444066 chore(version): version.json [skip ci] 2023-06-20 14:33:48 +00:00
Alireza 83c453d1f3 chore(version): update cs3d version and add iframe documentation (#3474) 2023-06-20 10:26:05 -04:00
Salim Kanoun fa73a458bd WIP (not tested yet) 2023-06-20 09:34:20 +00:00
ohif-bot 5670a1699d chore(version): Update package versions [skip ci] 2023-06-19 14:44:15 +00:00
ohif-bot c529c0da9c chore(version): version.json [skip ci] 2023-06-19 14:44:10 +00:00
dxlin d51211f6b9 fix(SR export): prevent empty sr description when export (#3173) 2023-06-19 10:35:53 -04:00
ohif-bot 2d8400f9d8 chore(version): Update package versions [skip ci] 2023-06-19 14:10:15 +00:00
ohif-bot e12242a15c chore(version): version.json [skip ci] 2023-06-19 14:10:11 +00:00
Davy 8baf9deaf0 fix(i18n): add more fields i18n (#3262) 2023-06-19 10:02:48 -04:00
ohif-bot a34a0b1a3a chore(version): Update package versions [skip ci] 2023-06-19 13:47:55 +00:00
ohif-bot e1545b67da chore(version): version.json [skip ci] 2023-06-19 13:47:52 +00:00
wangxuan d75305e70d fi(proptype): Invalid prop WhiteLabeling of type object supplied... (#3152) 2023-06-19 09:40:31 -04:00
ohif-bot 242beb7b46 chore(version): Update package versions [skip ci] 2023-06-19 13:39:11 +00:00
ohif-bot c5d4e97115 chore(version): version.json [skip ci] 2023-06-19 13:39:07 +00:00
Igor OctavianoandIgor Octaviano 59cdc4ba9a fix(SEG): Missing SpacingBetweenSlices prevents SEG rendering (#3344)
Co-authored-by: Igor Octaviano <igor.octaviano@radicalimaging.com>
2023-06-19 09:31:04 -04:00
ohif-bot 971c594791 chore(version): Update package versions [skip ci] 2023-06-19 13:22:44 +00:00
ohif-bot c55136f55e chore(version): version.json [skip ci] 2023-06-19 13:22:41 +00:00
Jemshith T K 63e12dd651 docs(ohif-cli): update ohif-cli.md (#3461) 2023-06-19 09:15:30 -04:00
ohif-bot c75c6af22f chore(version): Update package versions [skip ci] 2023-06-19 13:09:00 +00:00
ohif-bot 3c777ba58b chore(version): version.json [skip ci] 2023-06-19 13:08:56 +00:00
dxlin e6f73a2025 fix(RT) : Make ContourImageSequence check under ROIContourSequence->ContourImageSequence optional (#3476) 2023-06-19 09:01:23 -04:00
ohif-bot 652e61a417 chore(version): Update package versions [skip ci] 2023-06-15 17:08:06 +00:00
ohif-bot 48bcdd6b50 chore(version): version.json [skip ci] 2023-06-15 17:08:01 +00:00
Joe Boccanfuso cc73f15c8c fix(Button): redesign of existing Button component for consistency (#3473) 2023-06-15 12:58:21 -04:00
ohif-bot 3dce70e624 chore(version): Update package versions [skip ci] 2023-06-13 12:59:35 +00:00
ohif-bot 1d00893c49 chore(version): version.json [skip ci] 2023-06-13 12:59:31 +00:00
Sofien-Sellami 2890635675 fix(overlay): Fix undefined volumes in customizable viewport overlay (#3435) 2023-06-13 08:51:01 -04:00
ohif-bot a6d967a37e chore(version): Update package versions [skip ci] 2023-06-13 02:41:20 +00:00
ohif-bot f23a4500f5 chore(version): version.json [skip ci] 2023-06-13 02:41:17 +00:00
Alireza cadb559619 docs(readme): correct the docs images (#3470) 2023-06-12 22:33:54 -04:00
Salim Kanoun ad3ef3e8ef Merge branch 'master' into customizable-TS 2023-06-09 21:45:03 +02:00
Salim Kanoun 18c6a47dca Merge branch 'master' into customizable-TS 2023-06-07 13:08:49 +02:00
Salim Kanoun 93f30f06c2 Fix tests 2023-06-06 16:57:19 +02:00
Salim Kanoun 50d2640876 accept both accept header and transfer syntax uid 2023-06-05 22:32:29 +02:00
Salim Kanoun 10e399d7b8 Merge branch 'master' into customizable-TS 2023-06-04 12:43:16 +02:00
Salim Kanoun e4937e7473 update comments 2023-06-01 23:35:13 +02:00
Salim Kanoun 62dd2e78bd null safe 2023-06-01 23:32:16 +02:00
Salim Kanoun c3654b7ddf rework TS 2023-06-01 23:18:54 +02:00
Salim Kanoun de60586b71 fixes 2023-05-29 23:39:06 +02:00
Salim Kanoun f670678cf0 WIP accept header 2023-05-29 23:28:17 +02:00
Salim Kanoun 56633a67d9 Merge branch 'v3-stable' into customizable-TS 2023-05-29 23:26:36 +02:00
Salim Kanoun 8e2535a308 temp disabled of requested ts 2023-05-29 10:38:27 +02:00
Salim Kanoun 6da1734b6e Moving requested ts in createDicomWebApi (WIP) 2023-05-29 10:38:04 +02:00
Salim Kanoun 5ae6e9caf4 restaure config 2023-05-18 20:05:28 +02:00
Salim Kanoun 5e4043d99c add source of type correspondancies 2023-05-18 19:21:46 +02:00
Salim Kanoun 0e991d6bde config documentation 2023-05-18 19:13:36 +02:00
Salim Kanoun 2f63a9db80 init commit making TS customizable in OHIF settings 2023-05-18 19:06:26 +02:00
Salim Kanoun bedc379e97 Merge pull request #4 from OHIF/v3-stable
V3 stable
2023-05-18 17:35:16 +02:00
Salim Kanoun 5b7dc9e9c3 Merge pull request #3 from OHIF/v3-stable
V3 stable
2023-04-05 14:02:23 +02:00
Salim Kanoun 00099c0136 Merge remote-tracking branch 'upstream/v3-stable' into v3-stable 2023-03-12 08:01:06 +01:00
Salim Kanoun 799d648a90 Merge pull request #2 from OHIF/v3-stable
V3 stable
2022-12-28 21:25:05 +01:00
Salim Kanoun d5c347cecd Merge pull request #1 from OHIF/v3-stable
V3 stable
2022-11-24 17:01:48 +01:00
616 changed files with 6571 additions and 13770 deletions

No files matched your search

+2 -1
View File
@@ -1,5 +1,6 @@
server {
listen ${PORT};
listen ${PORT} default_server;
listen [::]:${PORT} default_server;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
@@ -0,0 +1,22 @@
server {
listen ${SSL_PORT} ssl http2 default_server;
listen [::]:${SSL_PORT} ssl http2 default_server;
ssl_certificate /etc/ssl/certs/ssl-certificate.crt;
ssl_certificate_key /etc/ssl/private/ssl-private-key.key;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
try_files $uri $uri/ /index.html;
add_header Cross-Origin-Opener-Policy same-origin;
add_header Cross-Origin-Embedder-Policy require-corp;
add_header Cross-Origin-Resource-Policy same-origin;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $http_x_forwarded_proto;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}
+11 -1
View File
@@ -1,6 +1,16 @@
#!/bin/sh
envsubst '${PORT}' < /usr/src/default.conf.template > /etc/nginx/conf.d/default.conf
if [ -n "$SSL_PORT" ]
then
envsubst '${SSL_PORT}:${PORT}' < /usr/src/default.ssl.conf.template > /etc/nginx/conf.d/default.conf
else
envsubst '${PORT}' < /usr/src/default.conf.template > /etc/nginx/conf.d/default.conf
fi
if [ -n "$APP_CONFIG" ]
then
echo "$APP_CONFIG" > /usr/share/nginx/html/app-config.js
fi
if [ -n "$CLIENT_ID" ] || [ -n "$HEALTHCARE_API_ENDPOINT" ]
then
+5 -1
View File
@@ -33,7 +33,11 @@ function transpileJavaScript(mode) {
rootMode: 'upward',
envName: mode,
cacheCompression: false,
cacheDirectory: true,
// Note: This was causing a lot of issues with yarn link of the cornerstone
// only set this to true if you don't have a yarn link to external libs
// otherwise expect the lib changes not to be reflected in the dev server
// as it will be cached
cacheDirectory: false,
},
};
}
+5 -9
View File
@@ -73,21 +73,17 @@ module.exports = (env, argv, { SRC_DIR, ENTRY }) => {
children: false,
warnings: true,
},
devServer: {
open: true,
port: 3000,
historyApiFallback: true,
headers: {
'Cross-Origin-Embedder-Policy': 'require-corp',
'Cross-Origin-Opener-Policy': 'same-origin',
},
},
cache: {
type: 'filesystem',
},
module: {
noParse: [/(codec)/, /(dicomicc)/],
rules: [
{
test: /\.js$/,
enforce: 'pre',
use: 'source-map-loader',
},
transpileJavaScriptRule(mode),
loadWebWorkersRule,
// loadShadersRule,
+6 -1
View File
@@ -61,7 +61,7 @@ RUN yarn run build
# Stage 3: Bundle the built application into a Docker container
# which runs Nginx using Alpine Linux
FROM nginxinc/nginx-unprivileged:1.23.1-alpine as final
FROM nginxinc/nginx-unprivileged:1.25-alpine as final
#RUN apk add --no-cache bash
ENV PORT=80
RUN rm /etc/nginx/conf.d/default.conf
@@ -69,5 +69,10 @@ USER nginx
COPY --chown=nginx:nginx .docker/Viewer-v3.x /usr/src
RUN chmod 777 /usr/src/entrypoint.sh
COPY --from=builder /usr/src/app/platform/app/dist /usr/share/nginx/html
# In entrypoint.sh, app-config.js might be overwritten, so chmod it to be writeable.
# The nginx user cannot chmod it, so change to root.
USER root
RUN chmod 666 /usr/share/nginx/html/app-config.js
USER nginx
ENTRYPOINT ["/usr/src/entrypoint.sh"]
CMD ["nginx", "-g", "daemon off;"]
+11 -9
View File
@@ -3,7 +3,7 @@
<div align="center">
<h1>OHIF Medical Imaging Viewer</h1>
<p><strong>The OHIF Viewer</strong> is a zero-footprint medical image viewer
provided by the <a href="https://ohif.org/">Open Health Imaging Foundation (OHIF)</a>. It is a configurable and extensible progressive web application with out-of-the-box support for image archives which support <a href="https://www.dicomstandard.org/dicomweb/">DICOMweb</a>.</p>
provided by the <a href="https://ohif.org/">Open Health Imaging Foundation (OHIF)</a>. It is a configurable and extensible progressive web application with out-of-the-box support for image archives which support <a href="https://www.dicomstandard.org/using/dicomweb/">DICOMweb</a>.</p>
</div>
@@ -34,13 +34,15 @@ provided by the <a href="https://ohif.org/">Open Health Imaging Foundation (OHIF
<!-- [![All Contributors](https://img.shields.io/badge/all_contributors-10-orange.svg?style=flat-square)](#contributors) -->
<!-- prettier-ignore-end -->
| | |
| :-: | :--- |
| <img src="platform/docs/docs/assets/img/demo-measurements.jpg" alt="Measurement tracking" width="350"/> | Measurement Tracking |
| <img src="platform/docs/docs/assets/img/demo-segmentation.png" alt="Segmentations" width="350"/> | Labelmap Segmentations |
| <img src="platform/docs/docs/assets/img/demo-ptct.png" alt="Hanging Protocols" width="350"/> | Fusion and Custom Hanging protocols |
| <img src="platform/docs/docs/assets/img/demo-microscopy.png" alt="Microscopy" width="350"/> | Slide Microscopy |
| <img src="platform/docs/docs/assets/img/demo-volumeRendering.png" alt="Volume Rendering" width="350"/> | Volume Rendering |
| | | |
| :-: | :--- | :--- |
| <img src="platform/docs/docs/assets/img/demo-measurements.jpg" alt="Measurement tracking" width="350"/> | Measurement Tracking | [Demo](https://viewer.ohif.org/viewer?StudyInstanceUIDs=1.3.6.1.4.1.25403.345050719074.3824.20170125095438.5) |
| <img src="platform/docs/docs/assets/img/demo-segmentation.png" alt="Segmentations" width="350"/> | Labelmap Segmentations | [Demo](https://viewer.ohif.org/viewer?StudyInstanceUIDs=1.3.12.2.1107.5.2.32.35162.30000015050317233592200000046) |
| <img src="platform/docs/docs/assets/img/demo-ptct.png" alt="Hanging Protocols" width="350"/> | Fusion and Custom Hanging protocols | [Demo](https://viewer.ohif.org/tmtv?StudyInstanceUIDs=1.3.6.1.4.1.14519.5.2.1.7009.2403.334240657131972136850343327463) |
| <img src="platform/docs/docs/assets/img/demo-microscopy.png" alt="Microscopy" width="350"/> | Slide Microscopy | [Demo](https://viewer.ohif.org/microscopy?StudyInstanceUIDs=2.25.275741864483510678566144889372061815320) |
| <img src="platform/docs/docs/assets/img/demo-volumeRendering.png" alt="Volume Rendering" width="350"/> | Volume Rendering | [Demo](https://viewer.ohif.org/viewer?StudyInstanceUIDs=1.3.6.1.4.1.25403.345050719074.3824.20170125095438.5&hangingprotocolId=mprAnd3DVolumeViewport) |
## About
@@ -161,7 +163,7 @@ yarn install
These commands are available from the root directory. Each project directory
also supports a number of commands that can be found in their respective
`README.md` and `project.json` files.
`README.md` and `package.json` files.
| Yarn Commands | Description |
| ---------------------------- | ------------------------------------------------------------- |
+8 -4
View File
@@ -13,12 +13,16 @@ module.exports = {
'inline-react-svg',
{
svgo: {
plugins: extendDefaultPlugins([
plugins: [
{
name: 'removeViewBox',
active: false,
name: 'preset-default',
params: {
overrides: {
removeViewBox: false,
},
},
},
]),
],
},
},
],
+1 -1
View File
@@ -1 +1 @@
14369414cf0193f00243ae5c9f8415cf7079a753
72207e6c785269db70d8f717c06ab3b27b1d0dd9
+5 -5
View File
@@ -1,6 +1,6 @@
{
"name": "@ohif/extension-cornerstone-dicom-rt",
"version": "3.7.0-beta.2",
"version": "3.7.0-beta.41",
"description": "DICOM RT read workflow",
"author": "OHIF",
"license": "MIT",
@@ -31,10 +31,10 @@
"start": "yarn run dev"
},
"peerDependencies": {
"@ohif/core": "3.7.0-beta.2",
"@ohif/extension-cornerstone": "3.7.0-beta.2",
"@ohif/extension-default": "3.7.0-beta.2",
"@ohif/i18n": "3.7.0-beta.2",
"@ohif/core": "3.7.0-beta.41",
"@ohif/extension-cornerstone": "3.7.0-beta.41",
"@ohif/extension-default": "3.7.0-beta.41",
"@ohif/i18n": "3.7.0-beta.41",
"prop-types": "^15.6.2",
"react": "^17.0.2",
"react-dom": "^17.0.2",
@@ -181,12 +181,6 @@ export default async function loadRTStruct(
ContourGeometricType,
} = ContourSequenceArray[c];
const sopInstanceUID = ContourImageSequence.ReferencedSOPInstanceUID;
const imageId = _getImageId(imageIdSopInstanceUidPairs, sopInstanceUID);
if (!imageId) {
continue;
}
let isSupported = false;
const points = [];
@@ -1,3 +1,4 @@
import { ButtonEnums } from '@ohif/ui';
import hydrateRTDisplaySet from './_hydrateRT';
const RESPONSE = {
@@ -38,12 +39,12 @@ function _askHydrate(uiViewportDialogService, viewportIndex) {
const message = 'Do you want to open this Segmentation?';
const actions = [
{
type: 'secondary',
type: ButtonEnums.type.secondary,
text: 'No',
value: RESPONSE.CANCEL,
},
{
type: 'primary',
type: ButtonEnums.type.primary,
text: 'Yes',
value: RESPONSE.HYDRATE_SEG,
},
@@ -1,6 +1,6 @@
{
"name": "@ohif/extension-cornerstone-dicom-seg",
"version": "3.7.0-beta.2",
"version": "3.7.0-beta.41",
"description": "DICOM SEG read workflow",
"author": "OHIF",
"license": "MIT",
@@ -31,10 +31,10 @@
"start": "yarn run dev"
},
"peerDependencies": {
"@ohif/core": "3.7.0-beta.2",
"@ohif/extension-cornerstone": "3.7.0-beta.2",
"@ohif/extension-default": "3.7.0-beta.2",
"@ohif/i18n": "3.7.0-beta.2",
"@ohif/core": "3.7.0-beta.41",
"@ohif/extension-cornerstone": "3.7.0-beta.41",
"@ohif/extension-default": "3.7.0-beta.41",
"@ohif/i18n": "3.7.0-beta.41",
"prop-types": "^15.6.2",
"react": "^17.0.2",
"react-dom": "^17.0.2",
@@ -1,5 +1,5 @@
import React from 'react';
import { Input, Dialog } from '@ohif/ui';
import { Input, Dialog, ButtonEnums } from '@ohif/ui';
function callInputDialog(uiDialogService, label, callback) {
const dialogId = 'enter-segment-label';
@@ -29,8 +29,8 @@ function callInputDialog(uiDialogService, label, callback) {
noCloseButton: true,
onClose: () => uiDialogService.dismiss({ id: dialogId }),
actions: [
{ id: 'cancel', text: 'Cancel', type: 'primary' },
{ id: 'save', text: 'Confirm', type: 'secondary' },
{ id: 'cancel', text: 'Cancel', type: ButtonEnums.type.secondary },
{ id: 'save', text: 'Confirm', type: ButtonEnums.type.primary },
],
onSubmit: onSubmitHandler,
body: ({ value, setValue }) => {
@@ -1,3 +1,4 @@
import { ButtonEnums } from '@ohif/ui';
import hydrateSEGDisplaySet from './_hydrateSEG';
const RESPONSE = {
@@ -6,11 +7,7 @@ const RESPONSE = {
HYDRATE_SEG: 5,
};
function promptHydrateSEG({
servicesManager,
segDisplaySet,
viewportIndex,
}) {
function promptHydrateSEG({ servicesManager, segDisplaySet, viewportIndex }) {
const { uiViewportDialogService } = servicesManager.services;
return new Promise(async function(resolve, reject) {
@@ -36,12 +33,12 @@ function _askHydrate(uiViewportDialogService, viewportIndex) {
const message = 'Do you want to open this Segmentation?';
const actions = [
{
type: 'secondary',
type: ButtonEnums.type.secondary,
text: 'No',
value: RESPONSE.CANCEL,
},
{
type: 'primary',
type: ButtonEnums.type.primary,
text: 'Yes',
value: RESPONSE.HYDRATE_SEG,
},
@@ -71,7 +71,8 @@ function OHIFCornerstoneSEGViewport(props) {
const referencedDisplaySet = segDisplaySet.getReferenceDisplaySet();
const referencedDisplaySetMetadata = _getReferencedDisplaySetMetadata(
referencedDisplaySet
referencedDisplaySet,
segDisplaySet
);
referencedDisplaySetRef.current = {
@@ -343,10 +344,10 @@ function OHIFCornerstoneSEGViewport(props) {
patientSex: PatientSex || '',
patientAge: PatientAge || '',
MRN: PatientID || '',
thickness: SliceThickness ? `${SliceThickness.toFixed(2)}mm` : '',
thickness: SliceThickness ? `${parseFloat(SliceThickness).toFixed(2)}mm` : '',
spacing:
SpacingBetweenSlices !== undefined
? `${SpacingBetweenSlices.toFixed(2)}mm`
? `${parseFloat(SpacingBetweenSlices).toFixed(2)}mm`
: '',
scanner: ManufacturerModelName || '',
},
@@ -381,20 +382,28 @@ OHIFCornerstoneSEGViewport.defaultProps = {
customProps: {},
};
function _getReferencedDisplaySetMetadata(referencedDisplaySet) {
function _getReferencedDisplaySetMetadata(referencedDisplaySet, segDisplaySet) {
const {
SharedFunctionalGroupsSequence: [SharedFunctionalGroup],
} = segDisplaySet.instance;
const {
PixelMeasuresSequence: [PixelMeasures],
} = SharedFunctionalGroup;
const { SpacingBetweenSlices, SliceThickness } = PixelMeasures;
const image0 = referencedDisplaySet.images[0];
const referencedDisplaySetMetadata = {
PatientID: image0.PatientID,
PatientName: image0.PatientName,
PatientSex: image0.PatientSex,
PatientAge: image0.PatientAge,
SliceThickness: image0.SliceThickness,
SliceThickness: image0.SliceThickness || SliceThickness,
StudyDate: image0.StudyDate,
SeriesDescription: image0.SeriesDescription,
SeriesInstanceUID: image0.SeriesInstanceUID,
SeriesNumber: image0.SeriesNumber,
ManufacturerModelName: image0.ManufacturerModelName,
SpacingBetweenSlices: image0.SpacingBetweenSlices,
SpacingBetweenSlices: image0.SpacingBetweenSlices || SpacingBetweenSlices,
};
return referencedDisplaySetMetadata;
+8 -8
View File
@@ -1,6 +1,6 @@
{
"name": "@ohif/extension-cornerstone-dicom-sr",
"version": "3.7.0-beta.2",
"version": "3.7.0-beta.41",
"description": "OHIF extension for an SR Cornerstone Viewport",
"author": "OHIF",
"license": "MIT",
@@ -32,10 +32,10 @@
"test:unit:ci": "jest --ci --runInBand --collectCoverage --passWithNoTests"
},
"peerDependencies": {
"@ohif/core": "3.7.0-beta.2",
"@ohif/extension-cornerstone": "3.7.0-beta.2",
"@ohif/extension-measurement-tracking": "3.7.0-beta.2",
"@ohif/ui": "3.7.0-beta.2",
"@ohif/core": "3.7.0-beta.41",
"@ohif/extension-cornerstone": "3.7.0-beta.41",
"@ohif/extension-measurement-tracking": "3.7.0-beta.41",
"@ohif/ui": "3.7.0-beta.41",
"dcmjs": "^0.29.5",
"dicom-parser": "^1.8.9",
"hammerjs": "^2.0.8",
@@ -44,9 +44,9 @@
},
"dependencies": {
"@babel/runtime": "^7.20.13",
"@cornerstonejs/adapters": "^1.1.8",
"@cornerstonejs/core": "^1.1.8",
"@cornerstonejs/tools": "^1.1.8",
"@cornerstonejs/adapters": "^1.7.1",
"@cornerstonejs/core": "^1.7.1",
"@cornerstonejs/tools": "^1.7.1",
"classnames": "^2.3.2"
}
}
@@ -225,6 +225,7 @@ function OHIFCornerstoneSRViewport(props) {
}}
onElementEnabled={onElementEnabled}
initialImageIndex={initialImageIndex}
isJumpToMeasurementDisabled={true}
></Component>
);
}, [activeImageDisplaySetData, viewportIndex, measurementSelected]);
+8 -8
View File
@@ -1,6 +1,6 @@
{
"name": "@ohif/extension-cornerstone",
"version": "3.7.0-beta.2",
"version": "3.7.0-beta.41",
"description": "OHIF extension for Cornerstone",
"author": "OHIF",
"license": "MIT",
@@ -36,9 +36,9 @@
"@cornerstonejs/codec-libjpeg-turbo-8bit": "^1.2.2",
"@cornerstonejs/codec-openjpeg": "^1.2.2",
"@cornerstonejs/codec-openjph": "^2.4.2",
"@cornerstonejs/dicom-image-loader": "^1.1.8",
"@ohif/core": "3.7.0-beta.2",
"@ohif/ui": "3.7.0-beta.2",
"@cornerstonejs/dicom-image-loader": "^1.7.1",
"@ohif/core": "3.7.0-beta.41",
"@ohif/ui": "3.7.0-beta.41",
"dcmjs": "^0.29.6",
"dicom-parser": "^1.8.21",
"hammerjs": "^2.0.8",
@@ -52,10 +52,10 @@
},
"dependencies": {
"@babel/runtime": "^7.20.13",
"@cornerstonejs/adapters": "^1.1.8",
"@cornerstonejs/core": "^1.1.8",
"@cornerstonejs/streaming-image-volume-loader": "^1.1.8",
"@cornerstonejs/tools": "^1.1.8",
"@cornerstonejs/adapters": "^1.7.1",
"@cornerstonejs/core": "^1.7.1",
"@cornerstonejs/streaming-image-volume-loader": "^1.7.1",
"@cornerstonejs/tools": "^1.7.1",
"@kitware/vtk.js": "27.3.1",
"html2canvas": "^1.4.1",
"lodash.debounce": "4.0.8",
@@ -10,13 +10,7 @@ import {
utilities as csUtils,
} from '@cornerstonejs/core';
import { MeasurementService } from '@ohif/core';
import {
CinePlayer,
useCine,
useViewportGrid,
Notification,
useViewportDialog,
} from '@ohif/ui';
import { Notification, useViewportDialog } from '@ohif/ui';
import {
IStackViewport,
IVolumeViewport,
@@ -28,6 +22,7 @@ import './OHIFCornerstoneViewport.css';
import CornerstoneOverlays from './Overlays/CornerstoneOverlays';
import getSOPInstanceAttributes from '../utils/measurementServiceMappings/utils/getSOPInstanceAttributes';
import CornerstoneServices from '../types/CornerstoneServices';
import CinePlayer from '../components/CinePlayer';
const STACK = 'stack';
@@ -119,6 +114,7 @@ const OHIFCornerstoneViewport = React.memo(props => {
servicesManager,
onElementEnabled,
onElementDisabled,
isJumpToMeasurementDisabled,
// Note: you SHOULD NOT use the initialImageIdOrIndex for manipulation
// of the imageData in the OHIFCornerstoneViewport. This prop is used
// to set the initial state of the viewport's first image to render
@@ -126,10 +122,7 @@ const OHIFCornerstoneViewport = React.memo(props => {
} = props;
const [scrollbarHeight, setScrollbarHeight] = useState('100px');
const [{ isCineEnabled, cines }, cineService] = useCine();
const [{ activeViewportIndex }] = useViewportGrid();
const [enabledVPElement, setEnabledVPElement] = useState(null);
const elementRef = useRef();
const {
@@ -145,74 +138,6 @@ const OHIFCornerstoneViewport = React.memo(props => {
} = servicesManager.services as CornerstoneServices;
const [viewportDialogState] = useViewportDialog();
const cineHandler = () => {
if (!cines || !cines[viewportIndex] || !enabledVPElement) {
return;
}
const cine = cines[viewportIndex];
const isPlaying = cine.isPlaying || false;
const frameRate = cine.frameRate || 24;
const validFrameRate = Math.max(frameRate, 1);
if (isPlaying) {
cineService.playClip(enabledVPElement, {
framesPerSecond: validFrameRate,
});
} else {
cineService.stopClip(enabledVPElement);
}
};
useEffect(() => {
eventTarget.addEventListener(
Enums.Events.STACK_VIEWPORT_NEW_STACK,
cineHandler
);
return () => {
cineService.setCine({ id: viewportIndex, isPlaying: false });
eventTarget.removeEventListener(
Enums.Events.STACK_VIEWPORT_NEW_STACK,
cineHandler
);
};
}, [enabledVPElement]);
useEffect(() => {
if (!cines || !cines[viewportIndex] || !enabledVPElement) {
return;
}
cineHandler();
return () => {
if (enabledVPElement && cines?.[viewportIndex]?.isPlaying) {
cineService.stopClip(enabledVPElement);
}
};
}, [cines, viewportIndex, cineService, enabledVPElement, cineHandler]);
const cine = cines[viewportIndex];
const isPlaying = (cine && cine.isPlaying) || false;
const handleCineClose = () => {
toolbarService.recordInteraction({
groupId: 'MoreTools',
itemId: 'cine',
interactionType: 'toggle',
commands: [
{
commandName: 'toggleCine',
commandOptions: {},
context: 'CORNERSTONE',
},
],
});
};
// useCallback for scroll bar height calculation
const setImageScrollBarHeight = useCallback(() => {
const scrollbarHeight = `${elementRef.current.clientHeight - 20}px`;
@@ -434,7 +359,6 @@ const OHIFCornerstoneViewport = React.memo(props => {
displaySetOptions,
presentations
);
if (measurement) {
cs3DTools.annotation.selection.setAnnotationSelected(measurement.uid);
}
@@ -454,6 +378,10 @@ const OHIFCornerstoneViewport = React.memo(props => {
* the cache for jumping to see if there is any jump queued, then we jump to the correct slice.
*/
useEffect(() => {
if (isJumpToMeasurementDisabled) {
return;
}
const unsubscribeFromJumpToMeasurementEvents = _subscribeToJumpToMeasurementEvents(
measurementService,
displaySetService,
@@ -479,15 +407,14 @@ const OHIFCornerstoneViewport = React.memo(props => {
};
}, [displaySets, elementRef, viewportIndex]);
console.debug('OHIFCornerstoneViewport rendering');
return (
<React.Fragment>
<div className="viewport-wrapper">
<ReactResizeDetector
handleWidth
handleHeight
skipOnMount={true} // Todo: make these configurable
refreshMode={'debounce'}
refreshRate={200} // transition amount in side panel
refreshMode="debounce"
refreshRate={50} // Wait 50 ms after last move to render
onResize={onResize}
targetRef={elementRef.current}
/>
@@ -505,25 +432,11 @@ const OHIFCornerstoneViewport = React.memo(props => {
scrollbarHeight={scrollbarHeight}
servicesManager={servicesManager}
/>
{isCineEnabled && (
<CinePlayer
className="absolute left-1/2 -translate-x-1/2 bottom-3"
isPlaying={isPlaying}
onClose={handleCineClose}
onPlayPauseChange={isPlaying =>
cineService.setCine({
id: activeViewportIndex,
isPlaying,
})
}
onFrameRateChange={frameRate =>
cineService.setCine({
id: activeViewportIndex,
frameRate,
})
}
/>
)}
<CinePlayer
enabledVPElement={enabledVPElement}
viewportIndex={viewportIndex}
servicesManager={servicesManager}
/>
</div>
<div className="absolute w-full">
{viewportDialogState.viewportIndex === viewportIndex && (
@@ -654,7 +567,6 @@ function _jumpToMeasurement(
const viewportInfo = cornerstoneViewportService.getViewportInfoByIndex(
viewportIndex
);
if (enabledElement) {
// See how the jumpToSlice() of Cornerstone3D deals with imageIdx param.
const viewport = enabledElement.viewport as
@@ -716,6 +628,10 @@ function _jumpToMeasurement(
// Component displayName
OHIFCornerstoneViewport.displayName = 'OHIFCornerstoneViewport';
OHIFCornerstoneViewport.defaultProps = {
isJumpToMeasurementDisabled: false,
};
OHIFCornerstoneViewport.propTypes = {
viewportIndex: PropTypes.number.isRequired,
displaySets: PropTypes.array.isRequired,
@@ -724,6 +640,7 @@ OHIFCornerstoneViewport.propTypes = {
displaySetOptions: PropTypes.arrayOf(PropTypes.any),
servicesManager: PropTypes.object.isRequired,
onElementEnabled: PropTypes.func,
isJumpToMeasurementDisabled: PropTypes.bool,
// Note: you SHOULD NOT use the initialImageIdOrIndex for manipulation
// of the imageData in the OHIFCornerstoneViewport. This prop is used
// to set the initial state of the viewport's first image to render
@@ -379,7 +379,7 @@ function _getViewportInstance(viewportData, imageIndex) {
if (viewportData.viewportType === Enums.ViewportType.STACK) {
imageId = viewportData.data.imageIds[imageIndex];
} else if (viewportData.viewportType === Enums.ViewportType.ORTHOGRAPHIC) {
const volumes = viewportData.volumes;
const volumes = viewportData.data;
if (volumes && volumes.length == 1) {
const volume = volumes[0];
imageId = volume.imageIds[imageIndex];
+3 -5
View File
@@ -457,11 +457,9 @@ function commandsModule({
const { viewport } = enabledElement;
if (viewport instanceof StackViewport) {
const { invert } = viewport.getProperties();
viewport.setProperties({ invert: !invert });
viewport.render();
}
const { invert } = viewport.getProperties();
viewport.setProperties({ invert: !invert });
viewport.render();
},
resetViewport: () => {
const enabledElement = _getActiveViewportEnabledElement();
@@ -0,0 +1,107 @@
import React, { useEffect } from 'react';
import { CinePlayer, useCine, useViewportGrid } from '@ohif/ui';
import { Enums, eventTarget } from '@cornerstonejs/core';
function WrappedCinePlayer({
enabledVPElement,
viewportIndex,
servicesManager,
}) {
const { toolbarService, customizationService } = servicesManager.services;
const [{ isCineEnabled, cines }, cineService] = useCine();
const [{ activeViewportIndex }] = useViewportGrid();
const { component: CinePlayerComponent = CinePlayer } =
customizationService.get('cinePlayer') ?? {};
const handleCineClose = () => {
toolbarService.recordInteraction({
groupId: 'MoreTools',
itemId: 'cine',
interactionType: 'toggle',
commands: [
{
commandName: 'toggleCine',
commandOptions: {},
context: 'CORNERSTONE',
},
],
});
};
const cineHandler = () => {
if (!cines || !cines[viewportIndex] || !enabledVPElement) {
return;
}
const cine = cines[viewportIndex];
const isPlaying = cine.isPlaying || false;
const frameRate = cine.frameRate || 24;
const validFrameRate = Math.max(frameRate, 1);
if (isPlaying) {
cineService.playClip(enabledVPElement, {
framesPerSecond: validFrameRate,
});
} else {
cineService.stopClip(enabledVPElement);
}
};
useEffect(() => {
eventTarget.addEventListener(
Enums.Events.STACK_VIEWPORT_NEW_STACK,
cineHandler
);
return () => {
cineService.setCine({ id: viewportIndex, isPlaying: false });
eventTarget.removeEventListener(
Enums.Events.STACK_VIEWPORT_NEW_STACK,
cineHandler
);
};
}, [enabledVPElement]);
useEffect(() => {
if (!cines || !cines[viewportIndex] || !enabledVPElement) {
return;
}
cineHandler();
return () => {
if (enabledVPElement && cines?.[viewportIndex]?.isPlaying) {
cineService.stopClip(enabledVPElement);
}
};
}, [cines, viewportIndex, cineService, enabledVPElement, cineHandler]);
const cine = cines[viewportIndex];
const isPlaying = (cine && cine.isPlaying) || false;
return (
isCineEnabled && (
<CinePlayerComponent
className="absolute left-1/2 -translate-x-1/2 bottom-3"
isPlaying={isPlaying}
onClose={handleCineClose}
onPlayPauseChange={isPlaying =>
cineService.setCine({
id: activeViewportIndex,
isPlaying,
})
}
onFrameRateChange={frameRate =>
cineService.setCine({
id: activeViewportIndex,
frameRate,
})
}
/>
)
);
}
export default WrappedCinePlayer;
@@ -0,0 +1,3 @@
import CinePlayer from './CinePlayer';
export default CinePlayer;
@@ -5,7 +5,7 @@ import PropTypes from 'prop-types';
import classNames from 'classnames';
import DicomFileUploader from '../../utils/DicomFileUploader';
import DicomUploadProgress from './DicomUploadProgress';
import { Button } from '@ohif/ui';
import { Button, ButtonEnums } from '@ohif/ui';
import './DicomUpload.css';
type DicomUploadProps = {
@@ -46,12 +46,7 @@ function DicomUpload({
<Dropzone onDrop={onDrop} noDrag>
{({ getRootProps, getInputProps }) => (
<div {...getRootProps()}>
<Button
variant="contained"
color="primary"
disabled={false}
onClick={() => {}}
>
<Button disabled={false} onClick={() => {}}>
{'Add files'}
<input {...getInputProps()} />
</Button>
@@ -62,9 +57,7 @@ function DicomUpload({
{({ getRootProps, getInputProps }) => (
<div {...getRootProps()}>
<Button
variant="contained"
color="primaryDark"
border="primaryActive"
type={ButtonEnums.type.secondary}
disabled={false}
onClick={() => {}}
>
@@ -308,13 +308,7 @@ function DicomUploadProgress({
} ${
dicomFileUploaderArr.length > 1 ? 'files' : 'file'
} completed.`}</span>
<Button
variant="contained"
color="primary"
disabled={false}
className="ml-auto"
onClick={onComplete}
>
<Button disabled={false} className="ml-auto" onClick={onComplete}>
{'Close'}
</Button>
</>
@@ -27,7 +27,6 @@ const DicomUploadProgressItem = memo(
const [failedReason, setFailedReason] = useState('');
const [status, setStatus] = useState(dicomFileUploader.getStatus());
console.info(`${dicomFileUploader.getFileId()}`);
const isComplete = useCallback(() => {
return (
status === UploadStatus.Failed ||
-1
View File
@@ -24,7 +24,6 @@ import { toolNames } from './initCornerstoneTools';
import { getEnabledElement, reset as enabledElementReset } from './state';
import dicomLoaderService from './utils/dicomLoaderService';
import getActiveViewportEnabledElement from './utils/getActiveViewportEnabledElement';
import { registerColormap } from './utils/colormap/transferFunctionHelpers';
import { id } from './id';
import * as csWADOImageLoader from './initWADOImageLoader.js';
+3 -5
View File
@@ -26,6 +26,7 @@ import nthLoader from './utils/nthLoader';
import interleaveTopToBottom from './utils/interleaveTopToBottom';
import initContextMenu from './initContextMenu';
import initDoubleClick from './initDoubleClick';
import { CornerstoneServices } from './types';
// TODO: Cypress tests are currently grabbing this from the window?
window.cornerstone = cornerstone;
@@ -69,10 +70,7 @@ export default async function init({
const {
userAuthenticationService,
measurementService,
customizationService,
displaySetService,
uiDialogService,
uiModalService,
uiNotificationService,
cineService,
@@ -81,7 +79,7 @@ export default async function init({
toolGroupService,
viewportGridService,
stateSyncService,
} = servicesManager.services;
} = servicesManager.services as CornerstoneServices;
window.services = servicesManager.services;
window.extensionManager = extensionManager;
@@ -166,7 +164,7 @@ export default async function init({
prefetch: appConfig?.maxNumRequests?.prefetch || 10,
};
initWADOImageLoader(userAuthenticationService, appConfig);
initWADOImageLoader(userAuthenticationService, appConfig, extensionManager);
/* Measurement Service */
this.measurementServiceSource = connectToolsToMeasurementService(
@@ -5,7 +5,7 @@ import dicomImageLoader, {
webWorkerManager,
} from '@cornerstonejs/dicom-image-loader';
import dicomParser from 'dicom-parser';
import { errorHandler } from '@ohif/core';
import { errorHandler, utils } from '@ohif/core';
const { registerVolumeLoader } = volumeLoader;
@@ -35,7 +35,8 @@ function initWebWorkers(appConfig) {
export default function initWADOImageLoader(
userAuthenticationService,
appConfig
appConfig,
extensionManager
) {
dicomImageLoader.external.cornerstone = cornerstone;
dicomImageLoader.external.dicomParser = dicomParser;
@@ -55,18 +56,18 @@ export default function initWADOImageLoader(
convertFloatPixelDataToInt: false,
},
beforeSend: function(xhr) {
//TODO should be removed in the future and request emitted by DicomWebDataSource
const sourceConfig =
extensionManager.getActiveDataSource()?.[0].getConfig() ?? {};
const headers = userAuthenticationService.getAuthorizationHeader();
const acceptHeader = utils.generateAcceptHeader(
sourceConfig.acceptHeader,
sourceConfig.requestTransferSyntaxUID,
sourceConfig.omitQuotationForMultipartRequest
);
// Request:
// JPEG-LS Lossless (1.2.840.10008.1.2.4.80) if available, otherwise accept
// whatever transfer-syntax the origin server provides.
// For now we use image/jls and image/x-jls because some servers still use the old type
// http://dicom.nema.org/medical/dicom/current/output/html/part18.html
const xhrRequestHeaders = {
Accept: appConfig.omitQuotationForMultipartRequest
? 'multipart/related; type=application/octet-stream'
: 'multipart/related; type="application/octet-stream"',
// 'multipart/related; type="image/x-jls", multipart/related; type="image/jls"; transfer-syntax="1.2.840.10008.1.2.4.80", multipart/related; type="image/x-jls", multipart/related; type="application/octet-stream"; transfer-syntax=*',
Accept: acceptHeader,
};
if (headers) {
@@ -542,6 +542,7 @@ class SegmentationService extends PubSubService {
id: segmentationId,
displaySetInstanceUID: segDisplaySet.displaySetInstanceUID,
type: representationType,
label: segDisplaySet.SeriesDescription,
representationData: {
[LABELMAP]: {
volumeId: segmentationId,
@@ -789,6 +790,7 @@ class SegmentationService extends PubSubService {
id: segmentationId,
displaySetInstanceUID: rtDisplaySetUID,
type: representationType,
label: rtDisplaySet.SeriesDescription,
representationData: {
[CONTOUR]: {
geometryIds,
@@ -816,45 +818,54 @@ class SegmentationService extends PubSubService {
const segmentsCachedStats = {};
const initializeContour = async rtStructData => {
const { data, id, color, segmentIndex, geometryId } = rtStructData;
const geometry = await geometryLoader.createAndCacheGeometry(geometryId, {
geometryData: {
data,
id,
color,
frameOfReferenceUID: structureSet.frameOfReferenceUID,
// catch error instead of failing to allow loading to continue
try {
const geometry = await geometryLoader.createAndCacheGeometry(
geometryId,
{
geometryData: {
data,
id,
color,
frameOfReferenceUID: structureSet.frameOfReferenceUID,
segmentIndex,
},
type: csEnums.GeometryType.CONTOUR,
}
);
const contourSet = geometry.data;
const centroid = contourSet.getCentroid();
segmentsCachedStats[segmentIndex] = {
center: { world: centroid },
modifiedTime: rtDisplaySet.SeriesDate, // we use the SeriesDate as the modifiedTime since this is the first time we are creating the segmentation
};
segmentation.segments[segmentIndex] = {
label: id,
segmentIndex,
},
type: csEnums.GeometryType.CONTOUR,
});
color,
...SEGMENT_CONSTANT,
};
const contourSet = geometry.data;
const centroid = contourSet.getCentroid();
const numInitialized = Object.keys(segmentsCachedStats).length;
segmentsCachedStats[segmentIndex] = {
center: { world: centroid },
modifiedTime: rtDisplaySet.SeriesDate, // we use the SeriesDate as the modifiedTime since this is the first time we are creating the segmentation
};
// Calculate percentage completed
const percentComplete = Math.round(
(numInitialized / allRTStructData.length) * 100
);
segmentation.segments[segmentIndex] = {
label: id,
segmentIndex,
color,
...SEGMENT_CONSTANT,
};
const numInitialized = Object.keys(segmentsCachedStats).length;
// Calculate percentage completed
const percentComplete = Math.round(
(numInitialized / allRTStructData.length) * 100
);
this._broadcastEvent(EVENTS.SEGMENT_LOADING_COMPLETE, {
percentComplete,
// Note: this is not the geometryIds length since there might be
// some missing ROINumbers
numSegments: allRTStructData.length,
});
this._broadcastEvent(EVENTS.SEGMENT_LOADING_COMPLETE, {
percentComplete,
// Note: this is not the geometryIds length since there might be
// some missing ROINumbers
numSegments: allRTStructData.length,
});
} catch (e) {
console.warn(e);
}
};
const promiseArray = [];
@@ -15,7 +15,7 @@ const EVENTS = {
* sync group declared.
*/
export type SyncCreator = (
type: string,
id: string,
options?: Record<string, unknown>
) => Synchronizer;
@@ -83,7 +83,7 @@ export default class SyncGroupService {
* @param type is the type of the synchronizer to create
* @param creator
*/
public setSynchronizer(type: string, creator: SyncCreator): void {
public addSynchronizerType(type: string, creator: SyncCreator): void {
this.synchronizerCreators[type.toLowerCase()] = creator;
}
@@ -745,7 +745,7 @@ class CornerstoneViewportService extends PubSubService
const viewport = this.getCornerstoneViewport(viewportId);
const viewportCamera = viewport.getCamera();
if (viewport instanceof VolumeViewport) {
if (viewport instanceof VolumeViewport || viewport instanceof VolumeViewport3D) {
this._setVolumeViewport(viewport, viewportData, viewportInfo).then(() => {
if (keepCamera) {
viewport.setCamera(viewportCamera);
@@ -198,7 +198,9 @@ class ViewportInfo {
// via cornerstoneViewportService
let viewportData = this.getViewportData();
if (viewportData.viewportType === Enums.ViewportType.ORTHOGRAPHIC) {
if (viewportData.viewportType === Enums.ViewportType.ORTHOGRAPHIC ||
viewportData.viewportType === Enums.ViewportType.VOLUME_3D
) {
viewportData = viewportData as VolumeViewportData;
return viewportData.data.some(
({ displaySetInstanceUID: dsUID }) => dsUID === displaySetInstanceUID
@@ -1,9 +1,9 @@
import { Types } from '@ohif/core';
import ToolGroupService from './services/ToolGroupService';
import SyncGroupService from './services/SyncGroupService';
import SegmentationService from './services/SegmentationService';
import CornerstoneCacheService from './services/CornerstoneCacheService';
import CornerstoneViewportService from './services/ViewportService/CornerstoneViewportService';
import ToolGroupService from '../services/ToolGroupService';
import SyncGroupService from '../services/SyncGroupService';
import SegmentationService from '../services/SegmentationService';
import CornerstoneCacheService from '../services/CornerstoneCacheService';
import CornerstoneViewportService from '../services/ViewportService/CornerstoneViewportService';
interface CornerstoneServices extends Types.Services {
cornerstoneViewportService: CornerstoneViewportService;
@@ -1,5 +1,5 @@
import React from 'react';
import { Input, Dialog } from '@ohif/ui';
import { Input, Dialog, ButtonEnums } from '@ohif/ui';
/**
*
@@ -60,8 +60,8 @@ function callInputDialog(
noCloseButton: true,
onClose: () => uiDialogService.dismiss({ id: dialogId }),
actions: [
{ id: 'cancel', text: 'Cancel', type: 'primary' },
{ id: 'save', text: 'Save', type: 'secondary' },
{ id: 'cancel', text: 'Cancel', type: ButtonEnums.type.secondary },
{ id: 'save', text: 'Save', type: ButtonEnums.type.primary },
],
onSubmit: onSubmitHandler,
body: ({ value, setValue }) => {
@@ -1,6 +1,5 @@
import SUPPORTED_TOOLS from './constants/supportedTools';
import getSOPInstanceAttributes from './utils/getSOPInstanceAttributes';
import getModalityUnit from './utils/getModalityUnit';
import { utils } from '@ohif/core';
const CircleROI = {
@@ -112,8 +111,7 @@ function getMappedAnnotations(annotation, DisplaySetService) {
);
const { SeriesNumber } = displaySet;
const { mean, stdDev, max, area, Modality } = targetStats;
const unit = getModalityUnit(Modality);
const { mean, stdDev, max, area, Modality, modalityUnit } = targetStats;
annotations.push({
SeriesInstanceUID,
@@ -121,7 +119,7 @@ function getMappedAnnotations(annotation, DisplaySetService) {
SeriesNumber,
frameNumber,
Modality,
unit,
unit: modalityUnit,
mean,
stdDev,
max,
@@ -1,6 +1,5 @@
import SUPPORTED_TOOLS from './constants/supportedTools';
import getSOPInstanceAttributes from './utils/getSOPInstanceAttributes';
import getModalityUnit from './utils/getModalityUnit';
import { utils } from '@ohif/core';
const EllipticalROI = {
@@ -112,8 +111,7 @@ function getMappedAnnotations(annotation, displaySetService) {
);
const { SeriesNumber } = displaySet;
const { mean, stdDev, max, area, Modality } = targetStats;
const unit = getModalityUnit(Modality);
const { mean, stdDev, max, area, Modality, modalityUnit } = targetStats;
annotations.push({
SeriesInstanceUID,
@@ -121,7 +119,7 @@ function getMappedAnnotations(annotation, displaySetService) {
SeriesNumber,
frameNumber,
Modality,
unit,
unit: modalityUnit,
mean,
stdDev,
max,
@@ -1,6 +1,5 @@
import SUPPORTED_TOOLS from './constants/supportedTools';
import getSOPInstanceAttributes from './utils/getSOPInstanceAttributes';
import getModalityUnit from './utils/getModalityUnit';
import { utils } from '@ohif/core';
const RectangleROI = {
@@ -106,8 +105,7 @@ function getMappedAnnotations(annotation, DisplaySetService) {
);
const { SeriesNumber } = displaySet;
const { mean, stdDev, max, area, Modality } = targetStats;
const unit = getModalityUnit(Modality);
const { mean, stdDev, max, area, Modality, modalityUnit } = targetStats;
annotations.push({
SeriesInstanceUID,
@@ -115,7 +113,7 @@ function getMappedAnnotations(annotation, DisplaySetService) {
SeriesNumber,
frameNumber,
Modality,
unit,
unit: modalityUnit,
mean,
stdDev,
max,
@@ -1,11 +0,0 @@
function getModalityUnit(modality) {
if (modality === 'CT') {
return 'HU';
} else if (modality === 'PT') {
return 'SUV';
} else {
return '';
}
}
export default getModalityUnit;
@@ -4,11 +4,9 @@ import {
setAnnotationSelected,
getFirstAnnotationSelected,
} from './selection';
import getModalityUnit from './getModalityUnit';
import getSOPInstanceAttributes from './getSOPInstanceAttributes';
export {
getModalityUnit,
getHandlesFromPoints,
getSOPInstanceAttributes,
isAnnotationSelected,
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "@ohif/extension-default",
"version": "3.7.0-beta.2",
"version": "3.7.0-beta.41",
"description": "Common/default features and functionality for basic image viewing",
"author": "OHIF Core Team",
"license": "MIT",
@@ -30,8 +30,8 @@
"start": "yarn run dev"
},
"peerDependencies": {
"@ohif/core": "3.7.0-beta.2",
"@ohif/i18n": "3.7.0-beta.2",
"@ohif/core": "3.7.0-beta.41",
"@ohif/i18n": "3.7.0-beta.41",
"dcmjs": "^0.29.5",
"dicomweb-client": "^0.10.2",
"prop-types": "^15.6.2",
@@ -13,6 +13,7 @@ const mappings = {
let _store = {
urls: [],
studyInstanceUIDMap: new Map(), // map of urls to array of study instance UIDs
// {
// url: url1
// studies: [Study1, Study2], // if multiple studies
@@ -41,10 +42,10 @@ const findStudies = (key, value) => {
};
function createDicomJSONApi(dicomJsonConfig) {
const { name, wadoRoot } = dicomJsonConfig;
const { wadoRoot } = dicomJsonConfig;
const implementation = {
initialize: async ({ params, query, url }) => {
initialize: async ({ query, url }) => {
if (!url) url = query.get('url');
let metaData = getMetaDataByURL(url);
@@ -58,11 +59,7 @@ function createDicomJSONApi(dicomJsonConfig) {
}
const response = await fetch(url);
let data = await response.json();
const studyInstanceUIDs = data.studies.map(
study => study.StudyInstanceUID
);
const data = await response.json();
let StudyInstanceUID;
let SeriesInstanceUID;
@@ -89,12 +86,14 @@ function createDicomJSONApi(dicomJsonConfig) {
url,
studies: [...data.studies],
});
return studyInstanceUIDs;
_store.studyInstanceUIDMap.set(
url,
data.studies.map(study => study.StudyInstanceUID)
);
},
query: {
studies: {
mapParams: () => { },
mapParams: () => {},
search: async param => {
const [key, value] = Object.entries(param)[0];
const mappedParam = mappings[key];
@@ -252,12 +251,13 @@ function createDicomJSONApi(dicomJsonConfig) {
return imageIds;
},
getImageIdsForInstance({ instance, frame }) {
const imageIds = getImageId({
instance,
frame,
});
const imageIds = getImageId({ instance, frame });
return imageIds;
},
getStudyInstanceUIDs: ({ params, query }) => {
const url = query.get('url');
return _store.studyInstanceUIDMap.get(url);
},
};
return IWebApiDataSource.create(implementation);
}
@@ -41,25 +41,7 @@ function createDicomLocalApi(dicomLocalConfig) {
const { name } = dicomLocalConfig;
const implementation = {
initialize: ({ params, query }) => {
const { StudyInstanceUIDs: paramsStudyInstanceUIDs } = params;
const queryStudyInstanceUIDs = query.getAll('StudyInstanceUIDs');
const StudyInstanceUIDs =
queryStudyInstanceUIDs || paramsStudyInstanceUIDs;
const StudyInstanceUIDsAsArray =
StudyInstanceUIDs && Array.isArray(StudyInstanceUIDs)
? StudyInstanceUIDs
: [StudyInstanceUIDs];
// Put SRs at the end of series list to make sure images are loaded first
StudyInstanceUIDsAsArray.forEach(StudyInstanceUID => {
const study = DicomMetadataStore.getStudy(StudyInstanceUID);
study.series = study.series.sort(customSort);
});
return StudyInstanceUIDsAsArray;
},
initialize: ({ params, query }) => {},
query: {
studies: {
mapParams: () => {},
@@ -246,6 +228,29 @@ function createDicomLocalApi(dicomLocalConfig) {
deleteStudyMetadataPromise() {
console.log('deleteStudyMetadataPromise not implemented');
},
getStudyInstanceUIDs: ({ params, query }) => {
const { StudyInstanceUIDs: paramsStudyInstanceUIDs } = params;
const queryStudyInstanceUIDs = query.getAll('StudyInstanceUIDs');
const StudyInstanceUIDs =
queryStudyInstanceUIDs || paramsStudyInstanceUIDs;
const StudyInstanceUIDsAsArray =
StudyInstanceUIDs && Array.isArray(StudyInstanceUIDs)
? StudyInstanceUIDs
: [StudyInstanceUIDs];
// Put SRs at the end of series list to make sure images are loaded first
let isStudyInCache = false;
StudyInstanceUIDsAsArray.forEach(StudyInstanceUID => {
const study = DicomMetadataStore.getStudy(StudyInstanceUID);
if (study) {
study.series = study.series.sort(customSort);
isStudyInCache = true;
}
});
return isStudyInCache ? StudyInstanceUIDsAsArray : [];
},
};
return IWebApiDataSource.create(implementation);
}
@@ -51,74 +51,87 @@ const metadataProvider = classes.MetadataProvider;
* @param {string|bool} singlepart - indicates of the retrieves can fetch singlepart. Options are bulkdata, video, image or boolean true
*/
function createDicomWebApi(dicomWebConfig, userAuthenticationService) {
const {
qidoRoot,
wadoRoot,
enableStudyLazyLoad,
supportsFuzzyMatching,
supportsWildcard,
supportsReject,
staticWado,
singlepart,
} = dicomWebConfig;
const dicomWebConfigCopy = JSON.parse(JSON.stringify(dicomWebConfig));
const qidoConfig = {
url: qidoRoot,
staticWado,
singlepart,
headers: userAuthenticationService.getAuthorizationHeader(),
errorInterceptor: errorHandler.getHTTPErrorHandler(),
};
const wadoConfig = {
url: wadoRoot,
staticWado,
singlepart,
headers: userAuthenticationService.getAuthorizationHeader(),
errorInterceptor: errorHandler.getHTTPErrorHandler(),
};
// TODO -> Two clients sucks, but its better than 1000.
// TODO -> We'll need to merge auth later.
const qidoDicomWebClient = staticWado
? new StaticWadoClient(qidoConfig)
: new api.DICOMwebClient(qidoConfig);
const wadoDicomWebClient = staticWado
? new StaticWadoClient(wadoConfig)
: new api.DICOMwebClient(wadoConfig);
let dicomWebConfigCopy,
qidoConfig,
wadoConfig,
qidoDicomWebClient,
wadoDicomWebClient,
getAuthrorizationHeader,
generateWadoHeader;
const implementation = {
initialize: ({ params, query }) => {
const { StudyInstanceUIDs: paramsStudyInstanceUIDs } = params;
const queryStudyInstanceUIDs = utils.splitComma(
query.getAll('StudyInstanceUIDs')
);
if (
dicomWebConfig.onConfiguration &&
typeof dicomWebConfig.onConfiguration === 'function'
) {
dicomWebConfig = dicomWebConfig.onConfiguration(dicomWebConfig, {
params,
query,
});
}
const StudyInstanceUIDs =
(queryStudyInstanceUIDs.length && queryStudyInstanceUIDs) ||
paramsStudyInstanceUIDs;
const StudyInstanceUIDsAsArray =
StudyInstanceUIDs && Array.isArray(StudyInstanceUIDs)
? StudyInstanceUIDs
: [StudyInstanceUIDs];
return StudyInstanceUIDsAsArray;
dicomWebConfigCopy = JSON.parse(JSON.stringify(dicomWebConfig));
getAuthrorizationHeader = () => {
const xhrRequestHeaders = {};
const authHeaders = userAuthenticationService.getAuthorizationHeader();
if (authHeaders && authHeaders.Authorization) {
xhrRequestHeaders.Authorization = authHeaders.Authorization;
}
return xhrRequestHeaders;
};
generateWadoHeader = () => {
let authorizationHeader = getAuthrorizationHeader();
//Generate accept header depending on config params
let formattedAcceptHeader = utils.generateAcceptHeader(
dicomWebConfig.acceptHeader,
dicomWebConfig.requestTransferSyntaxUID,
dicomWebConfig.omitQuotationForMultipartRequest
);
return {
...authorizationHeader,
Accept: formattedAcceptHeader,
};
};
qidoConfig = {
url: dicomWebConfig.qidoRoot,
staticWado: dicomWebConfig.staticWado,
singlepart: dicomWebConfig.singlepart,
headers: userAuthenticationService.getAuthorizationHeader(),
errorInterceptor: errorHandler.getHTTPErrorHandler(),
};
wadoConfig = {
url: dicomWebConfig.wadoRoot,
staticWado: dicomWebConfig.staticWado,
singlepart: dicomWebConfig.singlepart,
headers: userAuthenticationService.getAuthorizationHeader(),
errorInterceptor: errorHandler.getHTTPErrorHandler(),
};
// TODO -> Two clients sucks, but its better than 1000.
// TODO -> We'll need to merge auth later.
qidoDicomWebClient = dicomWebConfig.staticWado
? new StaticWadoClient(qidoConfig)
: new api.DICOMwebClient(qidoConfig);
wadoDicomWebClient = dicomWebConfig.staticWado
? new StaticWadoClient(wadoConfig)
: new api.DICOMwebClient(wadoConfig);
},
query: {
studies: {
mapParams: mapParams.bind(),
search: async function(origParams) {
const headers = userAuthenticationService.getAuthorizationHeader();
if (headers) {
qidoDicomWebClient.headers = headers;
}
qidoDicomWebClient.headers = getAuthrorizationHeader();
const { studyInstanceUid, seriesInstanceUid, ...mappedParams } =
mapParams(origParams, {
supportsFuzzyMatching,
supportsWildcard,
supportsFuzzyMatching: dicomWebConfig.supportsFuzzyMatching,
supportsWildcard: dicomWebConfig.supportsWildcard,
}) || {};
const results = await qidoSearch(
@@ -135,11 +148,7 @@ function createDicomWebApi(dicomWebConfig, userAuthenticationService) {
series: {
// mapParams: mapParams.bind(),
search: async function(studyInstanceUid) {
const headers = userAuthenticationService.getAuthorizationHeader();
if (headers) {
qidoDicomWebClient.headers = headers;
}
qidoDicomWebClient.headers = getAuthrorizationHeader();
const results = await seriesInStudy(
qidoDicomWebClient,
studyInstanceUid
@@ -151,11 +160,7 @@ function createDicomWebApi(dicomWebConfig, userAuthenticationService) {
},
instances: {
search: (studyInstanceUid, queryParameters) => {
const headers = userAuthenticationService.getAuthorizationHeader();
if (headers) {
qidoDicomWebClient.headers = headers;
}
qidoDicomWebClient.headers = getAuthrorizationHeader();
qidoSearch.call(
undefined,
qidoDicomWebClient,
@@ -179,9 +184,16 @@ function createDicomWebApi(dicomWebConfig, userAuthenticationService) {
* or is already retrieved, or a promise to a URL for such use if a BulkDataURI
*/
directURL: params => {
return getDirectURL({ wadoRoot, singlepart }, params);
return getDirectURL(
{
wadoRoot: dicomWebConfig.wadoRoot,
singlepart: dicomWebConfig.singlepart,
},
params
);
},
bulkDataURI: async ({ StudyInstanceUID, BulkDataURI }) => {
qidoDicomWebClient.headers = getAuthrorizationHeader();
const options = {
multipart: false,
BulkDataURI,
@@ -200,18 +212,13 @@ function createDicomWebApi(dicomWebConfig, userAuthenticationService) {
sortFunction,
madeInClient = false,
} = {}) => {
const headers = userAuthenticationService.getAuthorizationHeader();
if (headers) {
wadoDicomWebClient.headers = headers;
}
if (!StudyInstanceUID) {
throw new Error(
'Unable to query for SeriesMetadata without StudyInstanceUID'
);
}
if (enableStudyLazyLoad) {
if (dicomWebConfig.enableStudyLazyLoad) {
return implementation._retrieveSeriesMetadataAsync(
StudyInstanceUID,
filters,
@@ -234,17 +241,12 @@ function createDicomWebApi(dicomWebConfig, userAuthenticationService) {
store: {
dicom: async (dataset, request) => {
const headers = userAuthenticationService.getAuthorizationHeader();
if (headers) {
wadoDicomWebClient.headers = headers;
}
wadoDicomWebClient.headers = getAuthrorizationHeader();
if (dataset instanceof ArrayBuffer) {
const options = {
datasets: [dataset],
request,
};
await wadoDicomWebClient.storeInstances(options);
} else {
const meta = {
@@ -282,7 +284,7 @@ function createDicomWebApi(dicomWebConfig, userAuthenticationService) {
madeInClient
) => {
const enableStudyLazyLoad = false;
wadoDicomWebClient.headers = generateWadoHeader();
// data is all SOPInstanceUIDs
const data = await retrieveStudyMetadata(
wadoDicomWebClient,
@@ -353,6 +355,7 @@ function createDicomWebApi(dicomWebConfig, userAuthenticationService) {
madeInClient = false
) => {
const enableStudyLazyLoad = true;
wadoDicomWebClient.headers = generateWadoHeader();
// Get Series
const {
preLoadData: seriesSummaryMetadata,
@@ -515,10 +518,26 @@ function createDicomWebApi(dicomWebConfig, userAuthenticationService) {
getConfig() {
return dicomWebConfigCopy;
},
getStudyInstanceUIDs({ params, query }) {
const { StudyInstanceUIDs: paramsStudyInstanceUIDs } = params;
const queryStudyInstanceUIDs = utils.splitComma(
query.getAll('StudyInstanceUIDs')
);
const StudyInstanceUIDs =
(queryStudyInstanceUIDs.length && queryStudyInstanceUIDs) ||
paramsStudyInstanceUIDs;
const StudyInstanceUIDsAsArray =
StudyInstanceUIDs && Array.isArray(StudyInstanceUIDs)
? StudyInstanceUIDs
: [StudyInstanceUIDs];
return StudyInstanceUIDsAsArray;
},
};
if (supportsReject) {
implementation.reject = dcm4cheeReject(wadoRoot);
if (dicomWebConfig.supportsReject) {
implementation.reject = dcm4cheeReject(dicomWebConfig.wadoRoot);
}
return IWebApiDataSource.create(implementation);
@@ -18,15 +18,6 @@ function createDicomWebProxyApi(
const implementation = {
initialize: async ({ params, query }) => {
let studyInstanceUIDs = [];
// there seem to be a couple of variations of the case for this parameter
const queryStudyInstanceUIDs =
query.get('studyInstanceUIDs') || query.get('studyInstanceUids');
if (!queryStudyInstanceUIDs) {
throw new Error(`No studyInstanceUids in request for '${name}'`);
}
const url = query.get('url');
if (!url) {
@@ -39,12 +30,11 @@ function createDicomWebProxyApi(
}
dicomWebDelegate = createDicomWebApi(
data.servers.dicomWeb[0],
data.servers.dicomWeb[0].configuration,
UserAuthenticationService
);
studyInstanceUIDs = queryStudyInstanceUIDs.split(';');
dicomWebDelegate.initialize({ params, query });
}
return studyInstanceUIDs;
},
query: {
studies: {
@@ -77,6 +67,18 @@ function createDicomWebProxyApi(
dicomWebDelegate.getImageIdsForDisplaySet(...args),
getImageIdsForInstance: (...args) =>
dicomWebDelegate.getImageIdsForInstance(...args),
getStudyInstanceUIDs({ params, query }) {
let studyInstanceUIDs = [];
// there seem to be a couple of variations of the case for this parameter
const queryStudyInstanceUIDs =
query.get('studyInstanceUIDs') || query.get('studyInstanceUids');
if (!queryStudyInstanceUIDs) {
throw new Error(`No studyInstanceUids in request for '${name}'`);
}
studyInstanceUIDs = queryStudyInstanceUIDs.split(';');
return studyInstanceUIDs;
},
};
return IWebApiDataSource.create(implementation);
}
@@ -2,7 +2,7 @@ import React from 'react';
import PropTypes from 'prop-types';
import { useTranslation } from 'react-i18next';
import { Button, ButtonGroup } from '@ohif/ui';
import { LegacyButton, ButtonGroup } from '@ohif/ui';
function ActionButtons({ onExportClick, onCreateReportClick }) {
const { t } = useTranslation('MeasurementTable');
@@ -10,12 +10,16 @@ function ActionButtons({ onExportClick, onCreateReportClick }) {
return (
<React.Fragment>
<ButtonGroup color="black" size="inherit">
<Button className="px-2 py-2 text-base" onClick={onExportClick}>
{/* TODO Revisit design of ButtonGroup later - for now use LegacyButton for its children.*/}
<LegacyButton className="px-2 py-2 text-base" onClick={onExportClick}>
{t('Export CSV')}
</Button>
<Button className="px-2 py-2 text-base" onClick={onCreateReportClick}>
</LegacyButton>
<LegacyButton
className="px-2 py-2 text-base"
onClick={onCreateReportClick}
>
{t('Create Report')}
</Button>
</LegacyButton>
</ButtonGroup>
</React.Fragment>
);
@@ -3,7 +3,7 @@ import classnames from 'classnames';
import { useNavigate } from 'react-router-dom';
import { useAppConfig } from '@state';
import { Button } from '@ohif/ui';
import { Button, ButtonEnums } from '@ohif/ui';
function DataSourceSelector() {
const [appConfig] = useAppConfig();
@@ -31,9 +31,12 @@ function DataSourceSelector() {
)
.map(ds => (
<div key={ds.sourceName}>
<h1 className="text-white">{ds.friendlyName}</h1>
<h1 className="text-white">
{ds.configuration?.friendlyName || ds.friendlyName}
</h1>
<Button
className={classnames('font-bold', 'ml-2')}
type={ButtonEnums.type.primary}
className={classnames('ml-2')}
onClick={() => {
navigate({
pathname: '/',
@@ -1,7 +1,13 @@
import React, { useEffect, useState } from 'react';
import PropTypes from 'prop-types';
import { utils, ServicesManager } from '@ohif/core';
import { MeasurementTable, Dialog, Input, useViewportGrid } from '@ohif/ui';
import {
MeasurementTable,
Dialog,
Input,
useViewportGrid,
ButtonEnums,
} from '@ohif/ui';
import ActionButtons from './ActionButtons';
import debounce from 'lodash.debounce';
@@ -189,9 +195,8 @@ export default function PanelMeasurementTable({
);
},
actions: [
// temp: swap button types until colors are updated
{ id: 'cancel', text: 'Cancel', type: 'primary' },
{ id: 'save', text: 'Save', type: 'secondary' },
{ id: 'cancel', text: 'Cancel', type: ButtonEnums.type.secondary },
{ id: 'save', text: 'Save', type: ButtonEnums.type.primary },
],
onSubmit: onSubmitHandler,
},
@@ -1,6 +1,6 @@
/* eslint-disable react/display-name */
import React from 'react';
import { Dialog, Input, Select } from '@ohif/ui';
import { ButtonEnums, Dialog, Input, Select } from '@ohif/ui';
export const CREATE_REPORT_DIALOG_RESPONSE = {
CANCEL: 0,
@@ -81,8 +81,8 @@ export default function createReportDialogPrompt(
noCloseButton: true,
onClose: _handleClose,
actions: [
{ id: 'cancel', text: 'Cancel', type: 'primary' },
{ id: 'save', text: 'Save', type: 'secondary' },
{ id: 'cancel', text: 'Cancel', type: ButtonEnums.type.secondary },
{ id: 'save', text: 'Save', type: ButtonEnums.type.primary },
],
// TODO: Should be on button press...
onSubmit: _handleFormSubmit,
+1 -1
View File
@@ -29,7 +29,7 @@ export default function Toolbar({ servicesManager }) {
return (
<>
{toolbarButtons.map((toolDef, index) => {
{toolbarButtons.map(toolDef => {
const { id, Component, componentProps } = toolDef;
// TODO: ...
+5 -5
View File
@@ -1,6 +1,6 @@
{
"name": "@ohif/extension-dicom-microscopy",
"version": "3.7.0-beta.2",
"version": "3.7.0-beta.41",
"description": "OHIF extension for DICOM microscopy",
"author": "Bill Wallace, md-prog",
"license": "MIT",
@@ -28,10 +28,10 @@
"start": "yarn run dev"
},
"peerDependencies": {
"@ohif/core": "3.7.0-beta.2",
"@ohif/extension-default": "3.7.0-beta.2",
"@ohif/i18n": "3.7.0-beta.2",
"@ohif/ui": "3.7.0-beta.2",
"@ohif/core": "3.7.0-beta.41",
"@ohif/extension-default": "3.7.0-beta.41",
"@ohif/i18n": "3.7.0-beta.41",
"@ohif/ui": "3.7.0-beta.41",
"prop-types": "^15.6.2",
"react": "^17.0.2",
"react-dom": "^17.0.2",
@@ -1,5 +1,5 @@
import React from 'react';
import { Input, Dialog } from '@ohif/ui';
import { Input, Dialog, ButtonEnums } from '@ohif/ui';
/**
*
@@ -14,7 +14,7 @@ export default function callInputDialog({
uiDialogService,
title = 'Annotation',
defaultValue = '',
callback = (value: string, action: string) => {}
callback = (value: string, action: string) => {},
}) {
const dialogId = 'microscopy-input-dialog';
@@ -43,8 +43,8 @@ export default function callInputDialog({
noCloseButton: true,
onClose: () => uiDialogService.dismiss({ id: dialogId }),
actions: [
{ id: 'cancel', text: 'Cancel', type: 'primary' },
{ id: 'save', text: 'Save', type: 'secondary' },
{ id: 'cancel', text: 'Cancel', type: ButtonEnums.type.secondary },
{ id: 'save', text: 'Save', type: ButtonEnums.type.primary },
],
onSubmit: onSubmitHandler,
body: ({ value, setValue }) => {
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "@ohif/extension-dicom-pdf",
"version": "3.7.0-beta.2",
"version": "3.7.0-beta.41",
"description": "OHIF extension for PDF display",
"author": "OHIF",
"license": "MIT",
@@ -28,8 +28,8 @@
"test:unit:ci": "jest --ci --runInBand --collectCoverage --passWithNoTests"
},
"peerDependencies": {
"@ohif/core": "3.7.0-beta.2",
"@ohif/ui": "3.7.0-beta.2",
"@ohif/core": "3.7.0-beta.41",
"@ohif/ui": "3.7.0-beta.41",
"dcmjs": "^0.29.5",
"dicom-parser": "^1.8.9",
"hammerjs": "^2.0.8",
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "@ohif/extension-dicom-video",
"version": "3.7.0-beta.2",
"version": "3.7.0-beta.41",
"description": "OHIF extension for video display",
"author": "OHIF",
"license": "MIT",
@@ -28,8 +28,8 @@
"test:unit:ci": "jest --ci --runInBand --collectCoverage --passWithNoTests"
},
"peerDependencies": {
"@ohif/core": "3.7.0-beta.2",
"@ohif/ui": "3.7.0-beta.2",
"@ohif/core": "3.7.0-beta.41",
"@ohif/ui": "3.7.0-beta.41",
"dcmjs": "^0.29.5",
"dicom-parser": "^1.8.9",
"hammerjs": "^2.0.8",
+8 -8
View File
@@ -1,6 +1,6 @@
{
"name": "@ohif/extension-measurement-tracking",
"version": "3.7.0-beta.2",
"version": "3.7.0-beta.41",
"description": "Tracking features and functionality for basic image viewing",
"author": "OHIF Core Team",
"license": "MIT",
@@ -30,11 +30,11 @@
"start": "yarn run dev"
},
"peerDependencies": {
"@cornerstonejs/core": "^1.1.8",
"@cornerstonejs/tools": "^1.1.8",
"@ohif/core": "3.7.0-beta.2",
"@ohif/extension-cornerstone-dicom-sr": "3.7.0-beta.2",
"@ohif/ui": "3.7.0-beta.2",
"@cornerstonejs/core": "^1.7.1",
"@cornerstonejs/tools": "^1.7.1",
"@ohif/core": "3.7.0-beta.41",
"@ohif/extension-cornerstone-dicom-sr": "3.7.0-beta.41",
"@ohif/ui": "3.7.0-beta.41",
"classnames": "^2.3.2",
"dcmjs": "^0.29.5",
"lodash.debounce": "^4.17.21",
@@ -46,8 +46,8 @@
},
"dependencies": {
"@babel/runtime": "^7.20.13",
"@ohif/ui": "3.7.0-beta.2",
"@xstate/react": "^0.8.1",
"@ohif/ui": "3.7.0-beta.41",
"@xstate/react": "^3.2.2",
"xstate": "^4.10.0"
}
}
@@ -1,6 +1,6 @@
/* eslint-disable react/display-name */
import React from 'react';
import { Dialog, Input } from '@ohif/ui';
import { ButtonEnums, Dialog, Input } from '@ohif/ui';
import RESPONSE from './PROMPT_RESPONSES';
export default function createReportDialogPrompt(uiDialogService) {
@@ -20,12 +20,19 @@ export default function createReportDialogPrompt(uiDialogService) {
* @param {string} param0.value - value from input field
*/
const _handleFormSubmit = ({ action, value }) => {
uiDialogService.dismiss({ id: dialogId });
switch (action.id) {
case 'save':
resolve({ action: RESPONSE.CREATE_REPORT, value: value.label });
// Only save if description is not blank otherwise ignore
if (value.label && value.label.trim() !== '') {
resolve({
action: RESPONSE.CREATE_REPORT,
value: value.label.trim(),
});
uiDialogService.dismiss({ id: dialogId });
}
break;
case 'cancel':
uiDialogService.dismiss({ id: dialogId });
resolve({ action: RESPONSE.CANCEL, value: undefined });
break;
}
@@ -43,8 +50,8 @@ export default function createReportDialogPrompt(uiDialogService) {
noCloseButton: true,
onClose: _handleClose,
actions: [
{ id: 'cancel', text: 'Cancel', type: 'primary' },
{ id: 'save', text: 'Save', type: 'secondary' },
{ id: 'cancel', text: 'Cancel', type: ButtonEnums.type.secondary },
{ id: 'save', text: 'Save', type: ButtonEnums.type.primary },
],
// TODO: Should be on button press...
onSubmit: _handleFormSubmit,
@@ -55,8 +62,8 @@ export default function createReportDialogPrompt(uiDialogService) {
};
const onKeyPressHandler = event => {
if (event.key === 'Enter') {
uiDialogService.dismiss({ id: dialogId });
resolve({ action: RESPONSE.CREATE_REPORT, value: value.label });
// Trigger form submit
_handleFormSubmit({ action: { id: 'save' }, value });
}
};
return (
@@ -1,3 +1,5 @@
import { ButtonEnums } from '@ohif/ui';
const RESPONSE = {
NO_NEVER: -1,
CANCEL: 0,
@@ -31,19 +33,19 @@ function _askTrackMeasurements(uiViewportDialogService, viewportIndex) {
const actions = [
{
id: 'prompt-begin-tracking-cancel',
type: 'cancel',
type: ButtonEnums.type.secondary,
text: 'No',
value: RESPONSE.CANCEL,
},
{
id: 'prompt-begin-tracking-no-do-not-ask-again',
type: 'secondary',
type: ButtonEnums.type.secondary,
text: 'No, do not ask again',
value: RESPONSE.NO_NEVER,
},
{
id: 'prompt-begin-tracking-yes',
type: 'primary',
type: ButtonEnums.type.primary,
text: 'Yes',
value: RESPONSE.SET_STUDY_AND_SERIES,
},
@@ -1,4 +1,5 @@
import { hydrateStructuredReport } from '@ohif/extension-cornerstone-dicom-sr';
import { ButtonEnums } from '@ohif/ui';
const RESPONSE = {
NO_NEVER: -1,
@@ -61,12 +62,12 @@ function _askTrackMeasurements(uiViewportDialogService, viewportIndex) {
'Do you want to continue tracking measurements for this study?';
const actions = [
{
type: 'secondary',
type: ButtonEnums.type.secondary,
text: 'No',
value: RESPONSE.CANCEL,
},
{
type: 'primary',
type: ButtonEnums.type.primary,
text: 'Yes',
value: RESPONSE.HYDRATE_REPORT,
},
@@ -1,3 +1,5 @@
import { ButtonEnums } from '@ohif/ui';
const RESPONSE = {
NO_NEVER: -1,
CANCEL: 0,
@@ -38,14 +40,18 @@ function _askShouldAddMeasurements(uiViewportDialogService, viewportIndex) {
const message =
'Do you want to add this measurement to the existing report?';
const actions = [
{ type: 'cancel', text: 'Cancel', value: RESPONSE.CANCEL },
{
type: 'secondary',
type: ButtonEnums.type.secondary,
text: 'Cancel',
value: RESPONSE.CANCEL,
},
{
type: ButtonEnums.type.primary,
text: 'Create new report',
value: RESPONSE.CREATE_REPORT,
},
{
type: 'primary',
type: ButtonEnums.type.primary,
text: 'Add to existing report',
value: RESPONSE.ADD_SERIES,
},
@@ -2,7 +2,7 @@ import React from 'react';
import PropTypes from 'prop-types';
import { useTranslation } from 'react-i18next';
import { Button, ButtonGroup } from '@ohif/ui';
import { Button, ButtonEnums } from '@ohif/ui';
function ActionButtons({ onExportClick, onCreateReportClick, disabled }) {
const { t } = useTranslation('MeasurementTable');
@@ -10,23 +10,18 @@ function ActionButtons({ onExportClick, onCreateReportClick, disabled }) {
return (
<React.Fragment>
<Button
className="text-base px-2 py-2"
size="initial"
variant={disabled ? 'disabled' : 'outlined'}
color="black"
border="primaryActive"
onClick={onExportClick}
disabled={disabled}
type={ButtonEnums.type.secondary}
size={ButtonEnums.size.small}
>
{t('Export')}
</Button>
<Button
className="ml-2 px-2 text-base"
variant={disabled ? 'disabled' : 'outlined'}
size="initial"
color="black"
border="primaryActive"
className="ml-2"
onClick={onCreateReportClick}
type={ButtonEnums.type.secondary}
size={ButtonEnums.size.small}
disabled={disabled}
>
{t('Create Report')}
@@ -6,6 +6,7 @@ import {
Dialog,
Input,
useViewportGrid,
ButtonEnums,
} from '@ohif/ui';
import { DicomMetadataStore, utils } from '@ohif/core';
import { useDebounce } from '@hooks';
@@ -217,9 +218,8 @@ function PanelMeasurementTableTracking({ servicesManager, extensionManager }) {
);
},
actions: [
// temp: swap button types until colors are updated
{ id: 'cancel', text: 'Cancel', type: 'primary' },
{ id: 'save', text: 'Save', type: 'secondary' },
{ id: 'cancel', text: 'Cancel', type: ButtonEnums.type.secondary },
{ id: 'save', text: 'Save', type: ButtonEnums.type.primary },
],
onSubmit: onSubmitHandler,
},
@@ -1,4 +1,5 @@
import React, { useState, useEffect } from 'react';
import { useTranslation } from 'react-i18next';
import PropTypes from 'prop-types';
import { utils } from '@ohif/core';
import {
@@ -6,6 +7,7 @@ import {
useImageViewer,
useViewportGrid,
Dialog,
ButtonEnums,
} from '@ohif/ui';
import { useTrackedMeasurements } from '../../getContextModule';
@@ -23,19 +25,20 @@ function PanelStudyBrowserTracking({
dataSource,
}) {
const {
measurementService,
displaySetService,
uiDialogService,
hangingProtocolService,
uiNotificationService,
} = servicesManager.services;
const { t } = useTranslation('Common');
// Normally you nest the components so the tree isn't so deep, and the data
// doesn't have to have such an intense shape. This works well enough for now.
// Tabs --> Studies --> DisplaySets --> Thumbnails
const { StudyInstanceUIDs } = useImageViewer();
const [
{ activeViewportIndex, viewports, numCols, numRows },
{ activeViewportIndex, viewports },
viewportGridService,
] = useViewportGrid();
const [
@@ -76,36 +79,6 @@ function PanelStudyBrowserTracking({
const activeViewportDisplaySetInstanceUIDs =
viewports[activeViewportIndex]?.displaySetInstanceUIDs;
useEffect(() => {
const added = measurementService.EVENTS.MEASUREMENT_ADDED;
const addedRaw = measurementService.EVENTS.RAW_MEASUREMENT_ADDED;
const subscriptions = [];
[added, addedRaw].forEach(evt => {
subscriptions.push(
measurementService.subscribe(evt, ({ source, measurement }) => {
const {
referenceSeriesUID: SeriesInstanceUID,
referenceStudyUID: StudyInstanceUID,
} = measurement;
sendTrackedMeasurementsEvent('SET_DIRTY', { SeriesInstanceUID });
sendTrackedMeasurementsEvent('TRACK_SERIES', {
viewportIndex: activeViewportIndex,
StudyInstanceUID,
SeriesInstanceUID,
});
}).unsubscribe
);
});
return () => {
subscriptions.forEach(unsub => {
unsub();
});
};
}, [measurementService, activeViewportIndex, sendTrackedMeasurementsEvent]);
const { trackedSeries } = trackedMeasurements.context;
// ~~ studyDisplayList
@@ -133,7 +106,7 @@ function PanelStudyBrowserTracking({
const actuallyMappedStudies = mappedStudies.map(qidoStudy => {
return {
studyInstanceUid: qidoStudy.StudyInstanceUID,
date: formatDate(qidoStudy.StudyDate),
date: formatDate(qidoStudy.StudyDate) || t('NoStudyDate'),
description: qidoStudy.StudyDescription,
modalities: qidoStudy.ModalitiesInStudy,
numInstances: qidoStudy.NumInstances,
@@ -507,11 +480,15 @@ function _mapDisplaySets(
</div>
),
actions: [
{ id: 'cancel', text: 'Cancel', type: 'secondary' },
{
id: 'cancel',
text: 'Cancel',
type: ButtonEnums.type.secondary,
},
{
id: 'yes',
text: 'Yes',
type: 'primary',
type: ButtonEnums.type.primary,
classes: ['reject-yes-button'],
},
],
@@ -22,17 +22,22 @@ function TrackedCornerstoneViewport(props) {
viewportOptions,
} = props;
const { t } = useTranslation('TrackedViewport');
const { t } = useTranslation('Common');
const {
measurementService,
cornerstoneViewportService,
viewportGridService,
} = servicesManager.services;
// Todo: handling more than one displaySet on the same viewport
const displaySet = displaySets[0];
const [trackedMeasurements] = useTrackedMeasurements();
const [
trackedMeasurements,
sendTrackedMeasurementsEvent,
] = useTrackedMeasurements();
const [isTracked, setIsTracked] = useState(false);
const [trackedMeasurementUID, setTrackedMeasurementUID] = useState(null);
const [viewportElem, setViewportElem] = useState(null);
@@ -54,6 +59,7 @@ function TrackedCornerstoneViewport(props) {
PatientAge,
SliceThickness,
SpacingBetweenSlices,
StudyDate,
ManufacturerModelName,
} = displaySet.images[0];
@@ -148,6 +154,55 @@ function TrackedCornerstoneViewport(props) {
};
}, [isTracked]);
/**
* The effect for listening to measurement service measurement added events
* and in turn firing an event to update the measurement tracking state machine.
* The TrackedCornerstoneViewport is the best place for this because when
* a measurement is added, at least one TrackedCornerstoneViewport will be in
* the DOM and thus can react to the events fired.
*/
useEffect(() => {
const added = measurementService.EVENTS.MEASUREMENT_ADDED;
const addedRaw = measurementService.EVENTS.RAW_MEASUREMENT_ADDED;
const subscriptions = [];
[added, addedRaw].forEach(evt => {
subscriptions.push(
measurementService.subscribe(evt, ({ source, measurement }) => {
const { activeViewportIndex } = viewportGridService.getState();
// Each TrackedCornerstoneViewport receives the MeasurementService's events.
// Only send the tracked measurements event for the active viewport to avoid
// sending it more than once.
if (viewportIndex === activeViewportIndex) {
const {
referenceStudyUID: StudyInstanceUID,
referenceSeriesUID: SeriesInstanceUID,
} = measurement;
sendTrackedMeasurementsEvent('SET_DIRTY', { SeriesInstanceUID });
sendTrackedMeasurementsEvent('TRACK_SERIES', {
viewportIndex,
StudyInstanceUID,
SeriesInstanceUID,
});
}
}).unsubscribe
);
});
return () => {
subscriptions.forEach(unsub => {
unsub();
});
};
}, [
measurementService,
sendTrackedMeasurementsEvent,
viewportIndex,
viewportGridService,
]);
function switchMeasurement(direction) {
const newTrackedMeasurementUID = _getNextMeasurementUID(
direction,
@@ -194,7 +249,8 @@ function TrackedCornerstoneViewport(props) {
getStatusComponent={() => _getStatusComponent(isTracked)}
studyData={{
label: viewportLabel,
studyDate: formatDate(SeriesDate), // TODO: This is series date. Is that ok?
studyDate:
formatDate(SeriesDate) || formatDate(StudyDate) || t('NoStudyDate'),
currentSeries: SeriesNumber, // TODO - switch entire currentSeries to be UID based or actual position based
seriesDescription: SeriesDescription,
patientInformation: {
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "@ohif/extension-test",
"version": "3.7.0-beta.2",
"version": "3.7.0-beta.41",
"description": "OHIF extension used inside e2e testing",
"author": "OHIF",
"license": "MIT",
@@ -28,8 +28,8 @@
"test:unit:ci": "jest --ci --runInBand --collectCoverage --passWithNoTests"
},
"peerDependencies": {
"@ohif/core": "3.7.0-beta.2",
"@ohif/ui": "3.7.0-beta.2",
"@ohif/core": "3.7.0-beta.41",
"@ohif/ui": "3.7.0-beta.41",
"dcmjs": "0.29.4",
"dicom-parser": "^1.8.9",
"hammerjs": "^2.0.8",
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "@ohif/extension-tmtv",
"version": "3.7.0-beta.2",
"version": "3.7.0-beta.41",
"description": "OHIF extension for Total Metabolic Tumor Volume",
"author": "OHIF",
"license": "MIT",
@@ -28,8 +28,8 @@
"test:unit:ci": "jest --ci --runInBand --collectCoverage --passWithNoTests"
},
"peerDependencies": {
"@ohif/core": "3.7.0-beta.2",
"@ohif/ui": "3.7.0-beta.2",
"@ohif/core": "3.7.0-beta.41",
"@ohif/ui": "3.7.0-beta.41",
"dcmjs": "^0.29.5",
"dicom-parser": "^1.8.9",
"hammerjs": "^2.0.8",
+1 -3
View File
@@ -225,9 +225,7 @@ export default function PanelPetSUV({ servicesManager, commandsManager }) {
value={metadata.SeriesTime || ''}
onChange={() => {}}
/>
<Button color="primary" onClick={updateMetadata}>
Reload Data
</Button>
<Button onClick={updateMetadata}>Reload Data</Button>
</div>
</div>
}
@@ -1,5 +1,5 @@
import React from 'react';
import { Button, ButtonGroup } from '@ohif/ui';
import { LegacyButton, ButtonGroup } from '@ohif/ui';
import { useTranslation } from 'react-i18next';
function ExportReports({ segmentations, tmtvValue, config, commandsManager }) {
@@ -9,8 +9,9 @@ function ExportReports({ segmentations, tmtvValue, config, commandsManager }) {
<>
{segmentations?.length ? (
<div className="flex justify-center mt-4 space-x-2">
{/* TODO Revisit design of ButtonGroup later - for now use LegacyButton for its children.*/}
<ButtonGroup color="black" size="inherit">
<Button
<LegacyButton
className="px-2 py-2 text-base"
disabled={tmtvValue === null}
onClick={() => {
@@ -22,10 +23,10 @@ function ExportReports({ segmentations, tmtvValue, config, commandsManager }) {
}}
>
{t('Export CSV')}
</Button>
</LegacyButton>
</ButtonGroup>
<ButtonGroup color="black" size="inherit">
<Button
<LegacyButton
className="px-2 py-2 text-base"
onClick={() => {
commandsManager.runCommand('createTMTVRTReport');
@@ -33,7 +34,7 @@ function ExportReports({ segmentations, tmtvValue, config, commandsManager }) {
disabled={tmtvValue === null}
>
{t('Create RT Report')}
</Button>
</LegacyButton>
</ButtonGroup>
</div>
) : null}
@@ -181,7 +181,6 @@ export default function PanelRoiThresholdSegmentation({
<div className="overflow-x-hidden overflow-y-auto invisible-scrollbar">
<div className="flex mx-4 my-4 mb-4 space-x-4">
<Button
color="primary"
onClick={() => {
setLabelmapLoading(true);
setTimeout(() => {
@@ -194,9 +193,7 @@ export default function PanelRoiThresholdSegmentation({
>
{labelmapLoading ? 'loading ...' : 'New Label'}
</Button>
<Button color="primary" onClick={handleROIThresholding}>
Run
</Button>
<Button onClick={handleROIThresholding}>Run</Button>
</div>
<div
className="flex items-center justify-around h-8 mb-2 border-t outline-none cursor-pointer select-none bg-secondary-dark first:border-0 border-secondary-light"
@@ -1,5 +1,5 @@
import React from 'react';
import { Input, Label, Select, Button, ButtonGroup } from '@ohif/ui';
import { Input, Label, Select, LegacyButton, ButtonGroup } from '@ohif/ui';
import { useTranslation } from 'react-i18next';
export const ROI_STAT = 'roi_stat';
@@ -38,8 +38,9 @@ function ROIThresholdConfiguration({ config, dispatch, runCommand }) {
/>
</div>
<div className="w-1/2">
{/* TODO Revisit design of ButtonGroup later - for now use LegacyButton for its children.*/}
<ButtonGroup>
<Button
<LegacyButton
size="initial"
className="px-2 py-2 text-base text-white"
color="primaryLight"
@@ -47,8 +48,8 @@ function ROIThresholdConfiguration({ config, dispatch, runCommand }) {
onClick={() => runCommand('setStartSliceForROIThresholdTool')}
>
{t('Start')}
</Button>
<Button
</LegacyButton>
<LegacyButton
size="initial"
color="primaryLight"
variant="outlined"
@@ -56,7 +57,7 @@ function ROIThresholdConfiguration({ config, dispatch, runCommand }) {
onClick={() => runCommand('setEndSliceForROIThresholdTool')}
>
{t('End')}
</Button>
</LegacyButton>
</ButtonGroup>
</div>
</div>
@@ -1,5 +1,5 @@
import React from 'react';
import { Input, Dialog } from '@ohif/ui';
import { Input, Dialog, ButtonEnums } from '@ohif/ui';
function segmentationItemEditHandler({ id, servicesManager }) {
const { segmentationService, uiDialogService } = servicesManager.services;
@@ -44,23 +44,20 @@ function segmentationItemEditHandler({ id, servicesManager }) {
}
};
return (
<div className="p-4 bg-primary-dark">
<Input
autoFocus
className="mt-2 bg-black border-primary-main"
type="text"
containerClassName="mr-2"
value={value.label}
onChange={onChangeHandler}
onKeyPress={onKeyPressHandler}
/>
</div>
<Input
autoFocus
className="bg-black border-primary-main"
type="text"
containerClassName="mr-2"
value={value.label}
onChange={onChangeHandler}
onKeyPress={onKeyPressHandler}
/>
);
},
actions: [
// temp: swap button types until colors are updated
{ id: 'cancel', text: 'Cancel', type: 'primary' },
{ id: 'save', text: 'Save', type: 'secondary' },
{ id: 'cancel', text: 'Cancel', type: ButtonEnums.type.secondary },
{ id: 'save', text: 'Save', type: ButtonEnums.type.primary },
],
onSubmit: onSubmitHandler,
},
+10 -1
View File
@@ -553,7 +553,16 @@ const commandsModule = ({
commandsManager.runCommand('setViewportColormap', {
viewportIndex,
displaySetInstanceUID: ptDisplaySet.displaySetInstanceUID,
colormap,
colormap: {
name: colormap,
// TODO: This opacity mapping matches that in hpViewports, but
// ideally making this editable in a side panel would be useful
opacity: [
{ value: 0, opacity: 0 },
{ value: 0.1, opacity: 0.9 },
{ value: 1, opacity: 0.95 },
],
},
});
viewports.push(
+18 -6
View File
@@ -254,16 +254,20 @@ const fusionAXIAL = {
id: 'ctDisplaySet',
},
{
id: 'ptDisplaySet',
options: {
colormap: {
name: 'hsv',
opacityMapping: [{ value: 0.1, opacity: 0.9 }],
opacity: [
{ value: 0, opacity: 0 },
{ value: 0.1, opacity: 0.9 },
{ value: 1, opacity: 0.95 },
],
},
voi: {
custom: 'getPTVOIRange',
},
},
id: 'ptDisplaySet',
},
],
};
@@ -310,16 +314,20 @@ const fusionSAGITTAL = {
id: 'ctDisplaySet',
},
{
id: 'ptDisplaySet',
options: {
colormap: {
name: 'hsv',
opacityMapping: [{ value: 0.1, opacity: 0.9 }],
opacity: [
{ value: 0, opacity: 0 },
{ value: 0.1, opacity: 0.9 },
{ value: 1, opacity: 0.95 },
],
},
voi: {
custom: 'getPTVOIRange',
},
},
id: 'ptDisplaySet',
},
],
};
@@ -366,16 +374,20 @@ const fusionCORONAL = {
id: 'ctDisplaySet',
},
{
id: 'ptDisplaySet',
options: {
colormap: {
name: 'hsv',
opacityMapping: [{ value: 0.1, opacity: 0.9 }],
opacity: [
{ value: 0, opacity: 0 },
{ value: 0.1, opacity: 0.9 },
{ value: 1, opacity: 0.95 },
],
},
voi: {
custom: 'getPTVOIRange',
},
},
id: 'ptDisplaySet',
},
],
};
+1 -1
View File
@@ -1,5 +1,5 @@
{
"version": "3.7.0-beta.2",
"version": "3.7.0-beta.41",
"packages": ["extensions/*", "platform/*", "modes/*"],
"npmClient": "yarn",
"useWorkspaces": true
+7 -7
View File
@@ -1,6 +1,6 @@
{
"name": "@ohif/mode-basic-dev-mode",
"version": "3.7.0-beta.2",
"version": "3.7.0-beta.41",
"description": "Basic OHIF Viewer Using Cornerstone",
"author": "OHIF",
"license": "MIT",
@@ -29,12 +29,12 @@
"test:unit:ci": "jest --ci --runInBand --collectCoverage --passWithNoTests"
},
"peerDependencies": {
"@ohif/core": "3.7.0-beta.2",
"@ohif/extension-cornerstone": "3.7.0-beta.2",
"@ohif/extension-cornerstone-dicom-sr": "3.7.0-beta.2",
"@ohif/extension-default": "3.7.0-beta.2",
"@ohif/extension-dicom-pdf": "3.7.0-beta.2",
"@ohif/extension-dicom-video": "3.7.0-beta.2"
"@ohif/core": "3.7.0-beta.41",
"@ohif/extension-cornerstone": "3.7.0-beta.41",
"@ohif/extension-cornerstone-dicom-sr": "3.7.0-beta.41",
"@ohif/extension-default": "3.7.0-beta.41",
"@ohif/extension-dicom-pdf": "3.7.0-beta.41",
"@ohif/extension-dicom-video": "3.7.0-beta.41"
},
"dependencies": {
"@babel/runtime": "^7.20.13"
+9 -9
View File
@@ -1,6 +1,6 @@
{
"name": "@ohif/mode-test",
"version": "3.7.0-beta.2",
"version": "3.7.0-beta.41",
"description": "Basic mode for testing",
"author": "OHIF",
"license": "MIT",
@@ -32,14 +32,14 @@
"test:unit:ci": "jest --ci --runInBand --collectCoverage --passWithNoTests"
},
"peerDependencies": {
"@ohif/core": "3.7.0-beta.2",
"@ohif/extension-cornerstone": "3.7.0-beta.2",
"@ohif/extension-cornerstone-dicom-sr": "3.7.0-beta.2",
"@ohif/extension-default": "3.7.0-beta.2",
"@ohif/extension-dicom-pdf": "3.7.0-beta.2",
"@ohif/extension-dicom-video": "3.7.0-beta.2",
"@ohif/extension-measurement-tracking": "3.7.0-beta.2",
"@ohif/extension-test": "3.7.0-beta.2"
"@ohif/core": "3.7.0-beta.41",
"@ohif/extension-cornerstone": "3.7.0-beta.41",
"@ohif/extension-cornerstone-dicom-sr": "3.7.0-beta.41",
"@ohif/extension-default": "3.7.0-beta.41",
"@ohif/extension-dicom-pdf": "3.7.0-beta.41",
"@ohif/extension-dicom-video": "3.7.0-beta.41",
"@ohif/extension-measurement-tracking": "3.7.0-beta.41",
"@ohif/extension-test": "3.7.0-beta.41"
},
"dependencies": {
"@babel/runtime": "^7.20.13"
+10 -10
View File
@@ -1,6 +1,6 @@
{
"name": "@ohif/mode-longitudinal",
"version": "3.7.0-beta.2",
"version": "3.7.0-beta.41",
"description": "Longitudinal Workflow",
"author": "OHIF",
"license": "MIT",
@@ -32,15 +32,15 @@
"test:unit:ci": "jest --ci --runInBand --collectCoverage --passWithNoTests"
},
"peerDependencies": {
"@ohif/core": "3.7.0-beta.2",
"@ohif/extension-cornerstone": "3.7.0-beta.2",
"@ohif/extension-cornerstone-dicom-rt": "3.7.0-beta.2",
"@ohif/extension-cornerstone-dicom-seg": "3.7.0-beta.2",
"@ohif/extension-cornerstone-dicom-sr": "3.7.0-beta.2",
"@ohif/extension-default": "3.7.0-beta.2",
"@ohif/extension-dicom-pdf": "3.7.0-beta.2",
"@ohif/extension-dicom-video": "3.7.0-beta.2",
"@ohif/extension-measurement-tracking": "3.7.0-beta.2"
"@ohif/core": "3.7.0-beta.41",
"@ohif/extension-cornerstone": "3.7.0-beta.41",
"@ohif/extension-cornerstone-dicom-rt": "3.7.0-beta.41",
"@ohif/extension-cornerstone-dicom-seg": "3.7.0-beta.41",
"@ohif/extension-cornerstone-dicom-sr": "3.7.0-beta.41",
"@ohif/extension-default": "3.7.0-beta.41",
"@ohif/extension-dicom-pdf": "3.7.0-beta.41",
"@ohif/extension-dicom-video": "3.7.0-beta.41",
"@ohif/extension-measurement-tracking": "3.7.0-beta.41"
},
"dependencies": {
"@babel/runtime": "^7.20.13"
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "@ohif/mode-microscopy",
"version": "3.7.0-beta.2",
"version": "3.7.0-beta.41",
"description": "OHIF mode for DICOM microscopy",
"author": "OHIF",
"license": "MIT",
@@ -33,8 +33,8 @@
"test:unit:ci": "jest --ci --runInBand --collectCoverage --passWithNoTests"
},
"peerDependencies": {
"@ohif/core": "3.7.0-beta.2",
"@ohif/extension-dicom-microscopy": "3.7.0-beta.2"
"@ohif/core": "3.7.0-beta.41",
"@ohif/extension-dicom-microscopy": "3.7.0-beta.41"
},
"dependencies": {
"@babel/runtime": "^7.20.13"
+8 -8
View File
@@ -1,6 +1,6 @@
{
"name": "@ohif/mode-tmtv",
"version": "3.7.0-beta.2",
"version": "3.7.0-beta.41",
"description": "Total Metabolic Tumor Volume Workflow",
"author": "OHIF",
"license": "MIT",
@@ -32,13 +32,13 @@
"test:unit:ci": "jest --ci --runInBand --collectCoverage --passWithNoTests"
},
"peerDependencies": {
"@ohif/core": "3.7.0-beta.2",
"@ohif/extension-cornerstone": "3.7.0-beta.2",
"@ohif/extension-cornerstone-dicom-sr": "3.7.0-beta.2",
"@ohif/extension-default": "3.7.0-beta.2",
"@ohif/extension-dicom-pdf": "3.7.0-beta.2",
"@ohif/extension-dicom-video": "3.7.0-beta.2",
"@ohif/extension-measurement-tracking": "3.7.0-beta.2"
"@ohif/core": "3.7.0-beta.41",
"@ohif/extension-cornerstone": "3.7.0-beta.41",
"@ohif/extension-cornerstone-dicom-sr": "3.7.0-beta.41",
"@ohif/extension-default": "3.7.0-beta.41",
"@ohif/extension-dicom-pdf": "3.7.0-beta.41",
"@ohif/extension-dicom-video": "3.7.0-beta.41",
"@ohif/extension-measurement-tracking": "3.7.0-beta.41"
},
"dependencies": {
"@babel/runtime": "^7.20.13"
+2 -1
View File
@@ -220,7 +220,8 @@ function modeFactory({ modeConfiguration }) {
return {
id: ohif.layout,
props: {
// leftPanels: [ohif.thumbnailList],
leftPanels: [ohif.thumbnailList],
leftPanelDefaultClosed: true,
rightPanels: [tmtv.ROIThresholdPanel, tmtv.petSUV],
viewports: [
{
+1 -1
View File
@@ -113,7 +113,7 @@ function _initToolGroups(toolNames, Enums, toolGroupService, commandsManager) {
rotateIncrementDegrees: 0.1,
},
[toolNames.MipJumpToClick]: {
targetViewportIds: ['ptAXIAL', 'ptCORONAL', 'ptSAGITTAL'],
toolGroupId: toolGroupIds.PT,
},
};
+4 -12
View File
@@ -24,9 +24,8 @@ function _createButton(type, id, icon, label, commands, tooltip) {
function _createColormap(label, colormap) {
return {
id: label.toString(),
title: label,
subtitle: label,
id: label,
label,
type: 'action',
commands: [
{
@@ -36,13 +35,6 @@ function _createColormap(label, colormap) {
colormap,
},
},
{
commandName: 'setFusionPTColormap',
commandOptions: {
toolGroupId: toolGroupIds.Fusion,
colormap,
},
},
],
};
}
@@ -313,7 +305,8 @@ const toolbarButtons = [
commandName: 'displayNotification',
commandOptions: {
title: 'RectangleROI Threshold Tip',
text: 'RectangleROI Threshold tool should be used on PT Axial Viewport',
text:
'RectangleROI Threshold tool should be used on PT Axial Viewport',
type: 'info',
},
},
@@ -345,7 +338,6 @@ const toolbarButtons = [
tooltip: 'PET Image Colormap',
},
isAction: true, // ?
renderer: WindowLevelMenuItem,
items: [
_createColormap('HSV', 'hsv'),
_createColormap('Hot Iron', 'hot_iron'),
+2 -1
View File
@@ -74,6 +74,7 @@
"@babel/plugin-transform-runtime": "^7.17.0",
"@babel/plugin-transform-typescript": "^7.13.0",
"@babel/preset-env": "^7.16.11",
"@babel/plugin-proposal-private-property-in-object": "^7.21.11",
"@babel/preset-react": "^7.16.7",
"@babel/preset-typescript": "^7.13.0",
"@types/jest": "^27.5.0",
@@ -124,6 +125,7 @@
"semver": "^7.5.1",
"serve": "^14.2.0",
"shader-loader": "^1.3.1",
"source-map-loader": "^4.0.1",
"start-server-and-test": "^1.10.0",
"style-loader": "^1.0.0",
"terser-webpack-plugin": "^5.1.4",
@@ -149,7 +151,6 @@
]
},
"resolutions": {
"@cornerstonejs/core": "^1.1.8",
"**/@babel/runtime": "^7.20.13",
"commander": "8.3.0",
"nth-check": "^2.1.1",
-2
View File
@@ -186,8 +186,6 @@ module.exports = (env, argv) => {
chunkFilename: '[id].css',
})
);
} else {
mergedConfig.plugins.push(new webpack.HotModuleReplacementPlugin());
}
return mergedConfig;
@@ -1,5 +1,5 @@
describe('OHIF Context Menu', function () {
beforeEach(function () {
describe('OHIF Context Menu', function() {
beforeEach(function() {
cy.checkStudyRouteInViewer(
'1.2.840.113619.2.5.1762583153.215519.978957063.78'
);
@@ -10,10 +10,10 @@ describe('OHIF Context Menu', function () {
cy.resetViewport().wait(50);
});
it('checks context menu customization', function () {
it('checks context menu customization', function() {
// Add length measurement
cy.addLengthMeasurement();
cy.get('[data-cy="prompt-begin-tracking-yes"]').click();
cy.get('[data-cy="prompt-begin-tracking-yes-btn"]').click();
cy.get('[data-cy="measurement-item"]').click();
const [x1, y1] = [150, 100];
@@ -113,7 +113,7 @@ describe('OHIF Cornerstone Toolbar', () => {
cy.addLengthMeasurement();
cy.get('[data-cy="viewport-notification"]').should('exist');
cy.get('[data-cy="viewport-notification"]').should('be.visible');
cy.get('[data-cy="prompt-begin-tracking-yes"]').click();
cy.get('[data-cy="prompt-begin-tracking-yes-btn"]').click();
//Verify the measurement exists in the table
cy.get('@measurementsPanel').should('be.visible');
@@ -27,7 +27,7 @@ describe('OHIF Measurement Panel', function() {
cy.addLengthMeasurement();
cy.get('[data-cy="viewport-notification"]').should('exist');
cy.get('[data-cy="viewport-notification"]').should('be.visible');
cy.get('[data-cy="prompt-begin-tracking-yes"]').click();
cy.get('[data-cy="prompt-begin-tracking-yes-btn"]').click();
cy.get('[data-cy="measurement-item"]').click();
cy.get('[data-cy="measurement-item"]')
@@ -46,7 +46,7 @@ describe('OHIF Measurement Panel', function() {
it('checks if image would jump when clicked on a measurement item', function() {
// Add length measurement
cy.addLengthMeasurement();
cy.get('[data-cy="prompt-begin-tracking-yes"]').click();
cy.get('[data-cy="prompt-begin-tracking-yes-btn"]').click();
cy.scrollToIndex(13);
+28 -50
View File
@@ -46,7 +46,7 @@ Cypress.Commands.add('openStudy', PatientName => {
cy.openStudyList();
cy.get('#filter-patientNameOrId').type(PatientName);
// cy.get('@getStudies').then(() => {
cy.wait(1000);
cy.waitQueryList();
cy.get('[data-cy="study-list-results"]', { timeout: 5000 })
.contains(PatientName)
@@ -65,7 +65,8 @@ Cypress.Commands.add(
) {
cy.openStudyInViewer(StudyInstanceUID, otherParams);
cy.waitDicomImage();
cy.wait(2000);
// Very short wait to ensure pending updates are handled
cy.wait(25);
}
});
}
@@ -78,6 +79,9 @@ Cypress.Commands.add(
}
);
Cypress.Commands.add('waitQueryList', () => {
cy.get('[data-querying="false"]');
});
/**
* Command to search for a Modality and open the study.
*
@@ -89,7 +93,7 @@ Cypress.Commands.add('openStudyModality', Modality => {
cy.get('#filter-accessionOrModalityOrDescription')
.type(Modality)
.wait(2000);
.waitQueryList();
cy.get('[data-cy="study-list-results"]')
.contains(Modality)
@@ -113,7 +117,7 @@ Cypress.Commands.add('openStudyList', () => {
// For some reason cypress 12.x does not like to stub the network request
// so we just wait herer for 1 second
// cy.wait('@getStudies');
cy.wait(1000);
cy.waitQueryList();
});
Cypress.Commands.add('waitStudyList', () => {
@@ -198,54 +202,27 @@ Cypress.Commands.add('expectMinimumThumbnails', (seriesToWait = 1) => {
});
//Command to wait DICOM image to load into the viewport
Cypress.Commands.add('waitDicomImage', (timeout = 50000) => {
const loaded = cy.isPageLoaded();
if (loaded) {
cy.window()
.its('cornerstone')
.then({ timeout }, $cornerstone => {
return new Cypress.Promise(resolve => {
const onEvent = renderedEvt => {
const element = renderedEvt.detail.element;
element.removeEventListener(
$cornerstone.Enums.Events.IMAGE_RENDERED,
onEvent
);
$cornerstone.eventTarget.removeEventListener(
$cornerstone.Enums.Events.IMAGE_RENDERED,
onEvent
);
resolve();
};
const onEnabled = enabledEvt => {
const element = enabledEvt.detail.element;
element.addEventListener(
$cornerstone.Enums.Events.IMAGE_RENDERED,
onEvent
);
$cornerstone.eventTarget.removeEventListener(
$cornerstone.Enums.Events.ELEMENT_ENABLED,
onEnabled
);
};
const enabledElements = $cornerstone.getEnabledElements();
if (enabledElements && enabledElements.length) {
// Sometimes the page finishes rendering before this gets run,
// if so, just resolve immediately.
resolve();
} else {
$cornerstone.eventTarget.addEventListener(
$cornerstone.Enums.Events.ELEMENT_ENABLED,
onEnabled
Cypress.Commands.add('waitDicomImage', () => {
cy.window()
.its('cornerstone')
.should($cornerstone => {
const enabled = $cornerstone.getEnabledElements();
if (enabled?.length) {
enabled.forEach((item, i) => {
if (
item.viewport.viewportStatus !==
$cornerstone.Enums.ViewportStatus.RENDERED
) {
throw new Error(
`Viewport ${i} in state ${item.viewport.viewportStatus}`
);
}
});
});
}
} else {
throw new Error('No enabled elements');
}
});
cy.log('DICOM image loaded');
});
//Command to reset and clear all the changes made to the viewport
@@ -401,7 +378,8 @@ Cypress.Commands.add('setLayout', (columns = 1, rows = 1) => {
.eq(columns - 1)
.click();
cy.wait(1000);
cy.wait(10);
cy.waitDicomImage();
});
function convertCanvas(documentClone) {
+2
View File
@@ -43,3 +43,5 @@
# COMMENT: For sharedArrayBuffer, see https://developer.chrome.com/blog/enabling-shared-array-buffer/
Cross-Origin-Embedder-Policy = "require-corp"
Cross-Origin-Opener-Policy = "same-origin"
# set CORP to cross-origin for anyone who wants to use the viewer in an iframe
Cross-Origin-Resource-Policy = "cross-origin"
+18 -18
View File
@@ -1,6 +1,6 @@
{
"name": "@ohif/app",
"version": "3.7.0-beta.2",
"version": "3.7.0-beta.41",
"productVersion": "3.4.0",
"description": "OHIF Viewer",
"author": "OHIF Contributors",
@@ -51,23 +51,23 @@
"@cornerstonejs/codec-libjpeg-turbo-8bit": "^1.2.2",
"@cornerstonejs/codec-openjpeg": "^1.2.2",
"@cornerstonejs/codec-openjph": "^2.4.2",
"@cornerstonejs/dicom-image-loader": "^1.1.8",
"@ohif/core": "3.7.0-beta.2",
"@ohif/extension-cornerstone": "3.7.0-beta.2",
"@ohif/extension-cornerstone-dicom-rt": "3.7.0-beta.2",
"@ohif/extension-cornerstone-dicom-seg": "3.7.0-beta.2",
"@ohif/extension-cornerstone-dicom-sr": "3.7.0-beta.2",
"@ohif/extension-default": "3.7.0-beta.2",
"@ohif/extension-dicom-microscopy": "3.7.0-beta.2",
"@ohif/extension-dicom-pdf": "3.7.0-beta.2",
"@ohif/extension-dicom-video": "3.7.0-beta.2",
"@ohif/extension-test": "3.7.0-beta.2",
"@ohif/i18n": "3.7.0-beta.2",
"@ohif/mode-basic-dev-mode": "3.7.0-beta.2",
"@ohif/mode-longitudinal": "3.7.0-beta.2",
"@ohif/mode-microscopy": "3.7.0-beta.2",
"@ohif/mode-test": "3.7.0-beta.2",
"@ohif/ui": "3.7.0-beta.2",
"@cornerstonejs/dicom-image-loader": "^1.7.1",
"@ohif/core": "3.7.0-beta.41",
"@ohif/extension-cornerstone": "3.7.0-beta.41",
"@ohif/extension-cornerstone-dicom-rt": "3.7.0-beta.41",
"@ohif/extension-cornerstone-dicom-seg": "3.7.0-beta.41",
"@ohif/extension-cornerstone-dicom-sr": "3.7.0-beta.41",
"@ohif/extension-default": "3.7.0-beta.41",
"@ohif/extension-dicom-microscopy": "3.7.0-beta.41",
"@ohif/extension-dicom-pdf": "3.7.0-beta.41",
"@ohif/extension-dicom-video": "3.7.0-beta.41",
"@ohif/extension-test": "3.7.0-beta.41",
"@ohif/i18n": "3.7.0-beta.41",
"@ohif/mode-basic-dev-mode": "3.7.0-beta.41",
"@ohif/mode-longitudinal": "3.7.0-beta.41",
"@ohif/mode-microscopy": "3.7.0-beta.41",
"@ohif/mode-test": "3.7.0-beta.41",
"@ohif/ui": "3.7.0-beta.41",
"@types/react": "^17.0.38",
"classnames": "^2.3.2",
"core-js": "^3.16.1",
+7 -5
View File
@@ -4,7 +4,7 @@ window.config = {
modes: [],
showStudyList: true,
// below flag is for performance reasons, but it might not work for all servers
omitQuotationForMultipartRequest: true,
showWarningMessageForCrossOrigin: true,
showCPUFallbackMessage: true,
showLoadingIndicator: true,
@@ -13,10 +13,10 @@ window.config = {
defaultDataSourceName: 'dicomweb',
dataSources: [
{
friendlyName: 'dcmjs DICOMWeb Server',
namespace: '@ohif/extension-default.dataSourcesModule.dicomweb',
sourceName: 'dicomweb',
configuration: {
friendlyName: 'dcmjs DICOMWeb Server',
name: 'DCM4CHEE',
// Something here to check build
wadoUriRoot: 'https://myserver.com/dicomweb',
@@ -30,21 +30,23 @@ window.config = {
supportsFuzzyMatching: false,
supportsWildcard: false,
staticWado: true,
omitQuotationForMultipartRequest: true,
},
},
{
friendlyName: 'dicom json',
namespace: '@ohif/extension-default.dataSourcesModule.dicomjson',
sourceName: 'dicomjson',
configuration: {
friendlyName: 'dicom json',
name: 'json',
},
},
{
friendlyName: 'dicom local',
namespace: '@ohif/extension-default.dataSourcesModule.dicomlocal',
sourceName: 'dicomlocal',
configuration: {},
configuration: {
friendlyName: 'dicom local',
},
},
],
httpErrorHandler: error => {
+7 -7
View File
@@ -11,7 +11,6 @@ window.config = {
// some windows systems have issues with more than 3 web workers
maxNumberOfWebWorkers: 3,
// below flag is for performance reasons, but it might not work for all servers
omitQuotationForMultipartRequest: true,
showWarningMessageForCrossOrigin: true,
showCPUFallbackMessage: true,
showLoadingIndicator: true,
@@ -37,10 +36,10 @@ window.config = {
// },
dataSources: [
{
friendlyName: 'dcmjs DICOMWeb Server',
namespace: '@ohif/extension-default.dataSourcesModule.dicomweb',
sourceName: 'dicomweb',
configuration: {
friendlyName: 'dcmjs DICOMWeb Server',
name: 'aws',
// old server
// wadoUriRoot: 'https://server.dcmjs.org/dcm4chee-arc/aets/DCM4CHEE/wado',
@@ -51,7 +50,6 @@ window.config = {
wadoUriRoot: 'https://d33do7qe4w26qo.cloudfront.net/dicomweb',
qidoRoot: 'https://d33do7qe4w26qo.cloudfront.net/dicomweb',
wadoRoot: 'https://d33do7qe4w26qo.cloudfront.net/dicomweb',
qidoSupportsIncludeField: false,
supportsReject: false,
imageRendering: 'wadors',
@@ -68,29 +66,31 @@ window.config = {
enabled: true,
relativeResolution: 'studies',
},
omitQuotationForMultipartRequest: true,
},
},
{
friendlyName: 'dicomweb delegating proxy',
namespace: '@ohif/extension-default.dataSourcesModule.dicomwebproxy',
sourceName: 'dicomwebproxy',
configuration: {
friendlyName: 'dicomweb delegating proxy',
name: 'dicomwebproxy',
},
},
{
friendlyName: 'dicom json',
namespace: '@ohif/extension-default.dataSourcesModule.dicomjson',
sourceName: 'dicomjson',
configuration: {
friendlyName: 'dicom json',
name: 'json',
},
},
{
friendlyName: 'dicom local',
namespace: '@ohif/extension-default.dataSourcesModule.dicomlocal',
sourceName: 'dicomlocal',
configuration: {},
configuration: {
friendlyName: 'dicom local',
},
},
],
httpErrorHandler: error => {
+2 -2
View File
@@ -4,17 +4,16 @@ window.config = {
extensions: [],
showStudyList: true,
// below flag is for performance reasons, but it might not work for all servers
omitQuotationForMultipartRequest: true,
showWarningMessageForCrossOrigin: true,
strictZSpacingForVolumeViewport: true,
showCPUFallbackMessage: true,
defaultDataSourceName: 'dicomweb',
dataSources: [
{
friendlyName: 'DCM4CHEE Server',
namespace: '@ohif/extension-default.dataSourcesModule.dicomweb',
sourceName: 'dicomweb',
configuration: {
friendlyName: 'DCM4CHEE Server',
name: 'DCM4CHEE',
wadoUriRoot: 'https://d33do7qe4w26qo.cloudfront.net/dicomweb',
qidoRoot: 'https://d33do7qe4w26qo.cloudfront.net/dicomweb',
@@ -25,6 +24,7 @@ window.config = {
bulkDataURI: {
enabled: false,
},
omitQuotationForMultipartRequest: true,
},
},
],
@@ -4,7 +4,6 @@ window.config = {
modes: [],
showStudyList: true,
// below flag is for performance reasons, but it might not work for all servers
omitQuotationForMultipartRequest: true,
showWarningMessageForCrossOrigin: true,
showCPUFallbackMessage: true,
showLoadingIndicator: true,
@@ -13,10 +12,10 @@ window.config = {
defaultDataSourceName: 'dicomweb',
dataSources: [
{
friendlyName: 'dcmjs DICOMWeb Server',
namespace: '@ohif/extension-default.dataSourcesModule.dicomweb',
sourceName: 'dicomweb',
configuration: {
friendlyName: 'dcmjs DICOMWeb Server',
name: 'DCM4CHEE',
wadoUriRoot: 'http://localhost:5985',
qidoRoot: 'http://localhost:5985',
@@ -28,21 +27,23 @@ window.config = {
enableStudyLazyLoad: true,
supportsFuzzyMatching: false,
supportsWildcard: false,
omitQuotationForMultipartRequest: true,
},
},
{
friendlyName: 'dicom json',
namespace: '@ohif/extension-default.dataSourcesModule.dicomjson',
sourceName: 'dicomjson',
configuration: {
friendlyName: 'dicom json',
name: 'json',
},
},
{
friendlyName: 'dicom local',
namespace: '@ohif/extension-default.dataSourcesModule.dicomlocal',
sourceName: 'dicomlocal',
configuration: {},
configuration: {
friendlyName: 'dicom local',
},
},
],
};
@@ -5,7 +5,6 @@ window.config = {
showStudyList: true,
maxNumberOfWebWorkers: 3,
// below flag is for performance reasons, but it might not work for all servers
omitQuotationForMultipartRequest: true,
showWarningMessageForCrossOrigin: true,
showCPUFallbackMessage: true,
showLoadingIndicator: true,
@@ -14,10 +13,10 @@ window.config = {
defaultDataSourceName: 'dicomweb',
dataSources: [
{
friendlyName: 'Static WADO Local Data',
namespace: '@ohif/extension-default.dataSourcesModule.dicomweb',
sourceName: 'dicomweb',
configuration: {
friendlyName: 'Static WADO Local Data',
name: 'DCM4CHEE',
wadoUriRoot: '/dicomweb',
qidoRoot: '/dicomweb',
@@ -31,21 +30,23 @@ window.config = {
supportsWildcard: true,
staticWado: true,
singlepart: 'bulkdata,video,pdf',
omitQuotationForMultipartRequest: true,
},
},
{
friendlyName: 'dicom json',
namespace: '@ohif/extension-default.dataSourcesModule.dicomjson',
sourceName: 'dicomjson',
configuration: {
friendlyName: 'dicom json',
name: 'json',
},
},
{
friendlyName: 'dicom local',
namespace: '@ohif/extension-default.dataSourcesModule.dicomlocal',
sourceName: 'dicomlocal',
configuration: {},
configuration: {
friendlyName: 'dicom local',
},
},
],
httpErrorHandler: error => {
@@ -4,7 +4,6 @@ window.config = {
extensions: [],
modes: [],
// below flag is for performance reasons, but it might not work for all servers
omitQuotationForMultipartRequest: true,
showWarningMessageForCrossOrigin: true,
showCPUFallbackMessage: true,
showLoadingIndicator: true,
@@ -12,10 +11,10 @@ window.config = {
defaultDataSourceName: 'dicomweb',
dataSources: [
{
friendlyName: 'Orthanc Server',
namespace: '@ohif/extension-default.dataSourcesModule.dicomweb',
sourceName: 'dicomweb',
configuration: {
friendlyName: 'Orthanc Server',
name: 'Orthanc',
wadoUriRoot: '/wado',
qidoRoot: '/dicom-web',
@@ -23,21 +22,23 @@ window.config = {
qidoSupportsIncludeField: false,
imageRendering: 'wadors',
thumbnailRendering: 'wadors',
omitQuotationForMultipartRequest: true,
},
},
{
friendlyName: 'dicom json',
namespace: '@ohif/extension-default.dataSourcesModule.dicomjson',
sourceName: 'dicomjson',
configuration: {
friendlyName: 'dicom json',
name: 'json',
},
},
{
friendlyName: 'dicom local',
namespace: '@ohif/extension-default.dataSourcesModule.dicomlocal',
sourceName: 'dicomlocal',
configuration: {},
configuration: {
friendlyName: 'dicom local',
},
},
],
};
@@ -2,7 +2,6 @@ window.config = {
routerBasename: '/',
showStudyList: true,
// below flag is for performance reasons, but it might not work for all servers
omitQuotationForMultipartRequest: true,
showWarningMessageForCrossOrigin: true,
showCPUFallbackMessage: true,
showLoadingIndicator: true,
@@ -18,6 +17,7 @@ window.config = {
qidoSupportsIncludeField: false,
imageRendering: 'wadors',
thumbnailRendering: 'wadors',
omitQuotationForMultipartRequest: true,
// REQUIRED TAG:
// TODO: Remove tag after https://github.com/OHIF/ohif-core/pull/19 is merged and we bump version
// requestOptions: {
@@ -4,7 +4,7 @@ window.config = {
extensions: [],
modes: [],
// below flag is for performance reasons, but it might not work for all servers
omitQuotationForMultipartRequest: true,
showWarningMessageForCrossOrigin: true,
showCPUFallbackMessage: true,
showLoadingIndicator: true,
@@ -12,10 +12,10 @@ window.config = {
defaultDataSourceName: 'dicomweb',
dataSources: [
{
friendlyName: 'Orthanc Server',
namespace: '@ohif/extension-default.dataSourcesModule.dicomweb',
sourceName: 'dicomweb',
configuration: {
friendlyName: 'Orthanc Server',
name: 'Orthanc',
wadoUriRoot: 'http://127.0.0.1/pacs/wado',
qidoRoot: 'http://127.0.0.1/pacs/dicom-web',
@@ -23,21 +23,23 @@ window.config = {
qidoSupportsIncludeField: false,
imageRendering: 'wadors',
thumbnailRendering: 'wadors',
omitQuotationForMultipartRequest: true,
},
},
{
friendlyName: 'dicom json',
namespace: '@ohif/extension-default.dataSourcesModule.dicomjson',
sourceName: 'dicomjson',
configuration: {
friendlyName: 'dicom json',
name: 'json',
},
},
{
friendlyName: 'dicom local',
namespace: '@ohif/extension-default.dataSourcesModule.dicomlocal',
sourceName: 'dicomlocal',
configuration: {},
configuration: {
friendlyName: 'dicom local',
},
},
],
};
+8 -7
View File
@@ -4,7 +4,6 @@ window.config = {
modes: ['@ohif/mode-test'],
showStudyList: true,
// below flag is for performance reasons, but it might not work for all servers
omitQuotationForMultipartRequest: true,
maxNumberOfWebWorkers: 3,
showWarningMessageForCrossOrigin: false,
showCPUFallbackMessage: false,
@@ -13,10 +12,10 @@ window.config = {
defaultDataSourceName: 'e2e',
dataSources: [
{
friendlyName: 'StaticWado test data',
namespace: '@ohif/extension-default.dataSourcesModule.dicomweb',
sourceName: 'e2e',
configuration: {
friendlyName: 'StaticWado test data',
// The most important field to set for static WADO
staticWado: true,
name: 'StaticWADO',
@@ -31,13 +30,14 @@ window.config = {
supportsFuzzyMatching: false,
supportsWildcard: true,
singlepart: 'video,thumbnail,pdf',
omitQuotationForMultipartRequest: true,
},
},
{
friendlyName: 'Static WADO Local Data',
namespace: '@ohif/extension-default.dataSourcesModule.dicomweb',
sourceName: 'local',
configuration: {
friendlyName: 'Static WADO Local Data',
name: 'DCM4CHEE',
qidoRoot: '/dicomweb',
wadoRoot: '/dicomweb',
@@ -54,10 +54,10 @@ window.config = {
},
},
{
friendlyName: 'dcmjs DICOMWeb Server',
namespace: '@ohif/extension-default.dataSourcesModule.dicomweb',
sourceName: 'ohif',
configuration: {
friendlyName: 'dcmjs DICOMWeb Server',
name: 'aws',
// old server
// wadoUriRoot: 'https://server.dcmjs.org/dcm4chee-arc/aets/DCM4CHEE/wado',
@@ -98,18 +98,19 @@ window.config = {
// },
// },
{
friendlyName: 'dicom json',
namespace: '@ohif/extension-default.dataSourcesModule.dicomjson',
sourceName: 'dicomjson',
configuration: {
friendlyName: 'dicom json',
name: 'json',
},
},
{
friendlyName: 'dicom local',
namespace: '@ohif/extension-default.dataSourcesModule.dicomlocal',
sourceName: 'dicomlocal',
configuration: {},
configuration: {
friendlyName: 'dicom local',
},
},
],
httpErrorHandler: error => {
+6 -5
View File
@@ -6,7 +6,6 @@ window.config = {
},
enableGoogleCloudAdapter: false,
// below flag is for performance reasons, but it might not work for all servers
omitQuotationForMultipartRequest: true,
showWarningMessageForCrossOrigin: true,
showCPUFallbackMessage: true,
showLoadingIndicator: true,
@@ -37,10 +36,10 @@ window.config = {
defaultDataSourceName: 'dicomweb',
dataSources: [
{
friendlyName: 'dcmjs DICOMWeb Server',
namespace: '@ohif/extension-default.dataSourcesModule.dicomweb',
sourceName: 'dicomweb',
configuration: {
friendlyName: 'dcmjs DICOMWeb Server',
name: 'GCP',
wadoUriRoot:
'https://healthcare.googleapis.com/v1/projects/ohif-cloud-healthcare/locations/us-east4/datasets/ohif-qa-dataset/dicomStores/ohif-qa-2/dicomWeb',
@@ -55,21 +54,23 @@ window.config = {
supportsFuzzyMatching: true,
supportsWildcard: false,
dicomUploadEnabled: true,
omitQuotationForMultipartRequest: true,
},
},
{
friendlyName: 'dicom json',
namespace: '@ohif/extension-default.dataSourcesModule.dicomjson',
sourceName: 'dicomjson',
configuration: {
friendlyName: 'dicom json',
name: 'json',
},
},
{
friendlyName: 'dicom local',
namespace: '@ohif/extension-default.dataSourcesModule.dicomlocal',
sourceName: 'dicomlocal',
configuration: {},
configuration: {
friendlyName: 'dicom local',
},
},
],
};
Loaded 100 of 616 files, more files were not shown because too many files have changed in this diff. Show more