Compare commits
147
Commits
v3.7.0-beta.66
...
v3.7.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
df32a05a4d | ||
|
|
e205bf5c4e | ||
|
|
5ddf8a1602 | ||
|
|
83fccad46b | ||
|
|
3f9ee1451b | ||
|
|
70ea13ec1f | ||
|
|
157b88c909 | ||
|
|
75c3f94177 | ||
|
|
6874d5f0b9 | ||
|
|
a3f2a1a7b0 | ||
|
|
8067f0c62c | ||
|
|
a894b6ffc5 | ||
|
|
c3a5847dcd | ||
|
|
132938bbe0 | ||
|
|
5ba63bd010 | ||
|
|
7c57f67844 | ||
|
|
dc929d3df5 | ||
|
|
5a3af83168 | ||
|
|
a9a6ad50ea | ||
|
|
f8455056cd | ||
|
|
f9c3d510d3 | ||
|
|
52f34c64d0 | ||
|
|
7d87766ac4 | ||
|
|
820bb83b70 | ||
|
|
0a42d573bb | ||
|
|
f9f33f74c1 | ||
|
|
6a5fe59136 | ||
|
|
4911e4796c | ||
|
|
349c503f04 | ||
|
|
a6a6aed361 | ||
|
|
40673f64b3 | ||
|
|
eab42c1032 | ||
|
|
a790be9f5a | ||
|
|
8bc12a37d0 | ||
|
|
29aeb070e0 | ||
|
|
cdb66dcdef | ||
|
|
1fd98d9220 | ||
|
|
748acaf9c1 | ||
|
|
3f27c3820f | ||
|
|
c1d5ee7e3f | ||
|
|
646bef1d93 | ||
|
|
83c001f8e4 | ||
|
|
ebe8f71da2 | ||
|
|
17f528bcde | ||
|
|
ed82511acc | ||
|
|
745050a28e | ||
|
|
953c564d98 | ||
|
|
c418185f0b | ||
|
|
29748d46a1 | ||
|
|
a755cce7f1 | ||
|
|
42b7d9983c | ||
|
|
28cec04ff4 | ||
|
|
01d843f267 | ||
|
|
15eca78b68 | ||
|
|
108383b9ef | ||
|
|
1b207f14d2 | ||
|
|
38cd3d8afc | ||
|
|
5e7fe91617 | ||
|
|
64f68f58c2 | ||
|
|
49bc346661 | ||
|
|
5cc1dc9b9c | ||
|
|
2a086a699a | ||
|
|
58f214bca5 | ||
|
|
4dc2acdefa | ||
|
|
4c88a4a2fd | ||
|
|
06facd8ea2 | ||
|
|
eb22328fc0 | ||
|
|
b33ad0c7e9 | ||
|
|
3415cf909f | ||
|
|
e36a604331 | ||
|
|
8a335bd03d | ||
|
|
b3429729f1 | ||
|
|
e2e806f188 | ||
|
|
1129c155d2 | ||
|
|
c9c57b04d3 | ||
|
|
d09938b72a | ||
|
|
a67d72de85 | ||
|
|
3dabee2342 | ||
|
|
66abdace30 | ||
|
|
dc73b18748 | ||
|
|
52da92fed4 | ||
|
|
cfbb3cfded | ||
|
|
86f54d0d07 | ||
|
|
485cf037a1 | ||
|
|
3d38122b84 | ||
|
|
8feb5ee3f9 | ||
|
|
1f906963e7 | ||
|
|
9a4aa1a5aa | ||
|
|
fcd323fa8c | ||
|
|
b6e33224c8 | ||
|
|
b8e519a4a9 | ||
|
|
0bf9949f45 | ||
|
|
3431b103e2 | ||
|
|
00918d86d3 | ||
|
|
c86b129f58 | ||
|
|
10ca35d5f4 | ||
|
|
a308f7b4b6 | ||
|
|
04ca10d877 | ||
|
|
85c899b399 | ||
|
|
48bbd6281a | ||
|
|
896866e9d0 | ||
|
|
a02a5ded9a | ||
|
|
2bd3b26a6a | ||
|
|
034a6d7696 | ||
|
|
2fc28978da | ||
|
|
221dedde5d | ||
|
|
807bd663b3 | ||
|
|
0318504597 | ||
|
|
d609ae1edb | ||
|
|
2132f0777e | ||
|
|
10ecd4997b | ||
|
|
2d7721cb58 | ||
|
|
c49b833274 | ||
|
|
3279df543f | ||
|
|
2737903386 | ||
|
|
9ccdaef812 | ||
|
|
8c6764a936 | ||
|
|
38af3112ec | ||
|
|
bd7f9592ea | ||
|
|
fd74e46280 | ||
|
|
74e62a1763 | ||
|
|
7eda0c5f46 | ||
|
|
15e1af2526 | ||
|
|
11ca5b6eae | ||
|
|
64079e0720 | ||
|
|
7f683a336b | ||
|
|
0d10f46b88 | ||
|
|
ca19baeafd | ||
|
|
3e73ddf4c1 | ||
|
|
c0fb2b4892 | ||
|
|
2a7787132a | ||
|
|
3cc35e7ed5 | ||
|
|
de0a742733 | ||
|
|
c158279a53 | ||
|
|
12acc396d6 | ||
|
|
a6e668f8d4 | ||
|
|
6dd74b6371 | ||
|
|
908fb8e14f | ||
|
|
94f7cfb08e | ||
|
|
72ec0bffb5 | ||
|
|
82c6c3c353 | ||
|
|
1d38fe30a4 | ||
|
|
666964b6d7 | ||
|
|
fa4ee5c559 | ||
|
|
cd58560e9c | ||
|
|
16afa740b6 | ||
|
|
6715ad9bd6 |
No files matched your search
+209
-164
@@ -13,22 +13,24 @@ version: 2.1
|
||||
##
|
||||
orbs:
|
||||
codecov: codecov/codecov@1.0.5
|
||||
cypress: cypress-io/cypress@1.26.0
|
||||
cypress: cypress-io/cypress@3.1.4
|
||||
|
||||
executors:
|
||||
# Custom executor to override Cypress config
|
||||
deploy-to-prod-executor:
|
||||
cypress-custom:
|
||||
description: |
|
||||
Single Docker container used to run Cypress Tests
|
||||
docker:
|
||||
- image: cimg/node:16.14
|
||||
environment:
|
||||
CYPRESS_BASE_URL: https://ohif-staging.netlify.com/
|
||||
chrome-and-pacs:
|
||||
docker:
|
||||
# Primary container image where all steps run.
|
||||
- image: 'cypress/browsers:node16.14.2-slim-chrome103-ff102'
|
||||
- image: cimg/node:<< parameters.node-version >>-browsers
|
||||
parameters:
|
||||
node-version:
|
||||
default: '18.16.1'
|
||||
description: |
|
||||
The version of Node to run your tests with.
|
||||
type: string
|
||||
|
||||
defaults: &defaults
|
||||
docker:
|
||||
- image: cimg/node:16.14-browsers
|
||||
- image: cimg/node:18.18
|
||||
environment:
|
||||
TERM: xterm # Enable colors in term
|
||||
QUICK_BUILD: true
|
||||
@@ -43,6 +45,7 @@ jobs:
|
||||
steps:
|
||||
# Update yarn
|
||||
- run: yarn -v
|
||||
- run: node --version
|
||||
# Checkout code and ALL Git Tags
|
||||
- checkout
|
||||
- restore_cache:
|
||||
@@ -90,43 +93,6 @@ jobs:
|
||||
file: '/home/circleci/repo/platform/core/coverage/reports'
|
||||
flags: 'core'
|
||||
|
||||
###
|
||||
# Workflow: PR_OPTIONAL_DOCKER_PUBLISH
|
||||
###
|
||||
DOCKER_PR_PUBLISH:
|
||||
<<: *defaults
|
||||
steps:
|
||||
# Enable yarn workspaces
|
||||
- run: yarn config set workspaces-experimental true
|
||||
|
||||
# Checkout code and ALL Git Tags
|
||||
- checkout
|
||||
- restore_cache:
|
||||
name: Restore Yarn and Cypress Package Cache
|
||||
keys:
|
||||
# when lock file changes, use increasingly general patterns to restore cache
|
||||
- yarn-packages-{{ checksum "yarn.lock" }}
|
||||
- yarn-packages-
|
||||
|
||||
- run:
|
||||
name: Install Dependencies
|
||||
command: yarn install --frozen-lockfile
|
||||
|
||||
- setup_remote_docker:
|
||||
docker_layer_caching: false
|
||||
|
||||
- run:
|
||||
name: Build and push Docker image
|
||||
command: |
|
||||
# Remove npm config
|
||||
rm -f ./.npmrc
|
||||
# Set our version number using vars
|
||||
echo $CIRCLE_BUILD_NUM
|
||||
# Build our image, auth, and push
|
||||
docker build --tag ohif/app:PR_BUILD-$CIRCLE_BUILD_NUM .
|
||||
echo $DOCKER_PWD | docker login -u $DOCKER_LOGIN --password-stdin
|
||||
docker push ohif/app:PR_BUILD-$CIRCLE_BUILD_NUM
|
||||
|
||||
###
|
||||
# Workflow: DEPLOY
|
||||
###
|
||||
@@ -177,62 +143,52 @@ jobs:
|
||||
- commit.txt
|
||||
- version.json
|
||||
|
||||
# DEPLOY_TO_DEV:
|
||||
# docker:
|
||||
# - image: circleci/node:16.14.0
|
||||
# environment:
|
||||
# TERM: xterm
|
||||
# NETLIFY_SITE_ID: 32708787-c9b0-4634-b50f-7ca41952da77
|
||||
# working_directory: ~/repo
|
||||
# steps:
|
||||
# - attach_workspace:
|
||||
# at: ~/repo
|
||||
# - run: cd .netlify && npm install
|
||||
# - run: cp .netlify/deploy-workflow/_redirects platform/app/dist/_redirects
|
||||
# - run: cd .netlify && npm run deploy
|
||||
|
||||
# DEPLOY_TO_STAGING:
|
||||
# docker:
|
||||
# - image: circleci/node:16.14.0
|
||||
# environment:
|
||||
# TERM: xterm
|
||||
# NETLIFY_SITE_ID: c7502ae3-b150-493c-8422-05701e44a969
|
||||
# working_directory: ~/repo
|
||||
# steps:
|
||||
# - attach_workspace:
|
||||
# at: ~/repo
|
||||
# - run: cd .netlify && npm install
|
||||
# - run: cp .netlify/deploy-workflow/_redirects platform/app/dist/_redirects
|
||||
# - run: cd .netlify && npm run deploy
|
||||
|
||||
# DEPLOY_TO_PRODUCTION:
|
||||
# docker:
|
||||
# - image: circleci/node:16.14.0
|
||||
# environment:
|
||||
# TERM: xterm
|
||||
# NETLIFY_SITE_ID: 79c4a5da-5c95-4dc9-84f7-45fd9dfe21b0
|
||||
# working_directory: ~/repo
|
||||
# steps:
|
||||
# - attach_workspace:
|
||||
# at: ~/repo
|
||||
# - run: cd .netlify && npm install
|
||||
# - run: cp .netlify/deploy-workflow/_redirects platform/app/dist/_redirects
|
||||
# - run: cd .netlify && npm run deploy
|
||||
|
||||
# DEPLOY_TO_RELEASE_DEV:
|
||||
# docker:
|
||||
# - image: circleci/node:16.14.0
|
||||
# environment:
|
||||
# TERM: xterm
|
||||
# NETLIFY_SITE_ID: 3270878-22
|
||||
# working_directory: ~/repo
|
||||
# steps:
|
||||
# - attach_workspace:
|
||||
# at: ~/repo
|
||||
# - run: cd .netlify && npm install
|
||||
# - run:
|
||||
# cp .netlify/deploy-workflow/_redirects platform/app/dist/_redirects
|
||||
# - run: cd .netlify && npm run deploy
|
||||
# just to make sure later on we can publish them
|
||||
BUILD_PACKAGES_QUICK:
|
||||
<<: *defaults
|
||||
steps:
|
||||
- run: yarn -v
|
||||
# Checkout code and ALL Git Tags
|
||||
- checkout
|
||||
- attach_workspace:
|
||||
at: ~/repo
|
||||
# Use increasingly general patterns to restore cache
|
||||
- restore_cache:
|
||||
name: Restore Yarn and Cypress Package Cache
|
||||
keys:
|
||||
- yarn-packages-{{ checksum "yarn.lock" }}
|
||||
- yarn-packages-
|
||||
- run:
|
||||
name: Install Dependencies
|
||||
command: yarn install --frozen-lockfile
|
||||
- save_cache:
|
||||
name: Save Yarn Package Cache
|
||||
paths:
|
||||
- ~/.cache/yarn
|
||||
key: yarn-packages-{{ checksum "yarn.lock" }}
|
||||
- run:
|
||||
name: Avoid hosts unknown for github
|
||||
command: |
|
||||
rm -rf ~/.ssh
|
||||
mkdir ~/.ssh/
|
||||
echo -e "Host github.com\n\tStrictHostKeyChecking no\n" > ~/.ssh/config
|
||||
git config --global user.email "danny.ri.brown+ohif-bot@gmail.com"
|
||||
git config --global user.name "ohif-bot"
|
||||
- run:
|
||||
name: Authenticate with NPM registry
|
||||
command: echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" > ~/repo/.npmrc
|
||||
- run:
|
||||
name: Increase the event emitter limit
|
||||
command: |
|
||||
node ./increaseEventEmitterLimit.mjs
|
||||
- run:
|
||||
name: build half of the packages (to avoid out of memory in circleci)
|
||||
command: |
|
||||
yarn run build:package-all
|
||||
- run:
|
||||
name: build the other half of the packages
|
||||
command: |
|
||||
yarn run build:package-all-1
|
||||
|
||||
###
|
||||
# Workflow: RELEASE
|
||||
@@ -282,6 +238,14 @@ jobs:
|
||||
name: build the other half of the packages
|
||||
command: |
|
||||
yarn run build:package-all-1
|
||||
- run:
|
||||
name: increase min time out
|
||||
command: |
|
||||
npm config set fetch-retry-mintimeout 20000
|
||||
- run:
|
||||
name: increase max time out
|
||||
command: |
|
||||
npm config set fetch-retry-maxtimeout 120000
|
||||
- run:
|
||||
name: publish package versions
|
||||
command: |
|
||||
@@ -362,71 +326,152 @@ jobs:
|
||||
docker push ohif/app:$IMAGE_VERSION_FULL
|
||||
fi
|
||||
|
||||
workflows:
|
||||
version: 2
|
||||
# This is copied from the Cypress orb since the default for cypress/run is node 16 and
|
||||
# we migrated to 18
|
||||
CYPRESS_CUSTOM_RUN:
|
||||
description: |
|
||||
A single, complete job to run Cypress end-to-end tests in your application.
|
||||
executor: cypress-custom
|
||||
parallelism: << parameters.parallelism >>
|
||||
parameters:
|
||||
cypress-cache-key:
|
||||
default: cypress-cache-{{ arch }}-{{ checksum "package.json" }}
|
||||
description: Cache key used to cache the Cypress binary.
|
||||
type: string
|
||||
cypress-cache-path:
|
||||
default: ~/.cache/Cypress
|
||||
description: |
|
||||
By default, this will cache the '~/.cache/Cypress' directory so that the Cypress binary is cached. You can override this by providing your own cache path.
|
||||
type: string
|
||||
cypress-command:
|
||||
default: npx cypress run
|
||||
description: Command used to run your Cypress tests
|
||||
type: string
|
||||
include-branch-in-node-cache-key:
|
||||
default: false
|
||||
description: |
|
||||
If true, this cache will only apply to runs within the same branch. (Adds -{{ .Branch }}- to the node cache key)
|
||||
type: boolean
|
||||
install-browsers:
|
||||
default: false
|
||||
description: |
|
||||
Cypress runs by default in the Electron browser. Use this flag to install additional browsers to run your tests in.
|
||||
This is only needed if you are passing the `--browser` flag in your `cypress-command`.
|
||||
This parameter leverages the `circleci/browser-tools` orb and includes Chrome and FireFox.
|
||||
If you need additional browser support you can set this to false and use an executor with a docker image
|
||||
that includes the browsers of your choosing. See https://hub.docker.com/r/cypress/browsers/tags
|
||||
type: boolean
|
||||
install-command:
|
||||
default: ''
|
||||
description: Overrides the default NPM command (npm ci)
|
||||
type: string
|
||||
node-cache-version:
|
||||
default: v1
|
||||
description:
|
||||
Change the default node cache version if you need to clear the cache for any reason.
|
||||
type: string
|
||||
package-manager:
|
||||
default: npm
|
||||
description: Select the default node package manager to use. NPM v5+ Required.
|
||||
enum:
|
||||
- npm
|
||||
- yarn
|
||||
- yarn-berry
|
||||
type: enum
|
||||
parallelism:
|
||||
default: 1
|
||||
description: |
|
||||
Number of Circle machines to use for load balancing, min 1
|
||||
(requires `parallel` and `record` flags in your `cypress-command`)
|
||||
type: integer
|
||||
post-install:
|
||||
default: ''
|
||||
description: |
|
||||
Additional commands to run after running install but before verifying Cypress and saving cache.
|
||||
type: string
|
||||
start-command:
|
||||
default: ''
|
||||
description: Command used to start your local dev server for Cypress to tests against
|
||||
type: string
|
||||
working-directory:
|
||||
default: ''
|
||||
description: Directory containing package.json
|
||||
type: string
|
||||
steps:
|
||||
- cypress/install:
|
||||
cypress-cache-key: << parameters.cypress-cache-key >>
|
||||
cypress-cache-path: << parameters.cypress-cache-path >>
|
||||
include-branch-in-node-cache-key: << parameters.include-branch-in-node-cache-key >>
|
||||
install-browsers: << parameters.install-browsers >>
|
||||
install-command: << parameters.install-command >>
|
||||
node-cache-version: << parameters.node-cache-version >>
|
||||
package-manager: << parameters.package-manager >>
|
||||
post-install: << parameters.post-install >>
|
||||
working-directory: << parameters.working-directory >>
|
||||
- cypress/run-tests:
|
||||
cypress-command: << parameters.cypress-command >>
|
||||
start-command: << parameters.start-command >>
|
||||
working-directory: << parameters.working-directory >>
|
||||
|
||||
workflows:
|
||||
PR_CHECKS:
|
||||
jobs:
|
||||
- UNIT_TESTS
|
||||
|
||||
# E2E: PWA
|
||||
- cypress/run:
|
||||
name: 'E2E: PWA'
|
||||
executor: chrome-and-pacs
|
||||
browser: chrome
|
||||
pre-steps:
|
||||
- run: |
|
||||
# Clear yarn cache; use yarn from image (update image to update yarn)
|
||||
rm -rf ~/.yarn
|
||||
yarn -v
|
||||
yarn: true
|
||||
record: true
|
||||
store_artifacts: true
|
||||
working_directory: platform/app
|
||||
build: yarn test:data
|
||||
start: yarn run test:e2e:serve
|
||||
spec: 'cypress/integration/**/*'
|
||||
wait-on: 'http://localhost:3000'
|
||||
cache-key: 'yarn-packages-{{ checksum "yarn.lock" }}'
|
||||
no-workspace: true # Don't persist workspace
|
||||
post-steps:
|
||||
- store_artifacts:
|
||||
path: platform/app/cypress/screenshots
|
||||
- store_artifacts:
|
||||
path: platform/app/cypress/videos
|
||||
- store_test_results:
|
||||
path: platform/app/cypress/results
|
||||
- BUILD_PACKAGES_QUICK:
|
||||
filters:
|
||||
branches:
|
||||
ignore: master
|
||||
- UNIT_TESTS:
|
||||
requires:
|
||||
- UNIT_TESTS
|
||||
|
||||
PR_OPTIONAL_VISUAL_TESTS:
|
||||
jobs:
|
||||
- AWAIT_APPROVAL:
|
||||
type: approval
|
||||
# Update hub.docker.org
|
||||
- cypress/run:
|
||||
name: 'Generate Percy Snapshots'
|
||||
executor: cypress/browsers-chrome76
|
||||
browser: chrome
|
||||
pre-steps:
|
||||
- run: 'rm -rf ~/.yarn && yarn -v && yarn global add wait-on'
|
||||
yarn: true
|
||||
store_artifacts: false
|
||||
working_directory: platform/app
|
||||
build:
|
||||
yarn test:data && npx cross-env QUICK_BUILD=true APP_CONFIG=config/dicomweb-server.js
|
||||
yarn run build
|
||||
# start server --> verify running --> percy + chrome + cypress
|
||||
command: yarn run test:e2e:dist
|
||||
cache-key: 'yarn-packages-{{ checksum "yarn.lock" }}'
|
||||
no-workspace: true # Don't persist workspace
|
||||
post-steps:
|
||||
- store_artifacts:
|
||||
path: platform/app/cypress/screenshots
|
||||
- store_artifacts:
|
||||
path: platform/app/cypress/videos
|
||||
- BUILD_PACKAGES_QUICK
|
||||
- CYPRESS_CUSTOM_RUN:
|
||||
name: 'Cypress Tests'
|
||||
context: cypress
|
||||
matrix:
|
||||
parameters:
|
||||
start-command:
|
||||
- yarn run test:data && yarn run test:e2e:serve
|
||||
install-browsers:
|
||||
- true
|
||||
cypress-command:
|
||||
- 'npx wait-on@latest http://localhost:3000 && cd platform/app && npx cypress run
|
||||
--record --browser chrome --parallel'
|
||||
package-manager:
|
||||
- 'yarn'
|
||||
cypress-cache-key:
|
||||
- 'yarn-packages-{{ checksum "yarn.lock" }}'
|
||||
cypress-cache-path:
|
||||
- '~/.cache/Cypress'
|
||||
requires:
|
||||
- AWAIT_APPROVAL
|
||||
- BUILD_PACKAGES_QUICK
|
||||
|
||||
# PR_OPTIONAL_VISUAL_TESTS:
|
||||
# jobs:
|
||||
# - AWAIT_APPROVAL:
|
||||
# type: approval
|
||||
# # Update hub.docker.org
|
||||
# - cypress/run:
|
||||
# name: 'Generate Percy Snapshots'
|
||||
# executor: cypress/browsers-chrome76
|
||||
# browser: chrome
|
||||
# pre-steps:
|
||||
# - run: 'rm -rf ~/.yarn && yarn -v && yarn global add wait-on'
|
||||
# yarn: true
|
||||
# store_artifacts: false
|
||||
# working_directory: platform/app
|
||||
# build:
|
||||
# yarn test:data && npx cross-env QUICK_BUILD=true APP_CONFIG=config/dicomweb-server.js
|
||||
# yarn run build
|
||||
# # start server --> verify running --> percy + chrome + cypress
|
||||
# command: yarn run test:e2e:dist
|
||||
# cache-key: 'yarn-packages-{{ checksum "yarn.lock" }}'
|
||||
# no-workspace: true # Don't persist workspace
|
||||
# post-steps:
|
||||
# - store_artifacts:
|
||||
# path: platform/app/cypress/screenshots
|
||||
# - store_artifacts:
|
||||
# path: platform/app/cypress/videos
|
||||
# requires:
|
||||
# - AWAIT_APPROVAL
|
||||
|
||||
# Our master branch deploys to viewer-dev.ohif.org, the viewer.ohif.org is
|
||||
# deployed from the release branch which is more stable and less frequently updated.
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
[codespell]
|
||||
skip = .git,*.pdf,*.svg,yarn.lock,*.min.js,locales
|
||||
# ignore words ending with … and some camelcased variables and names
|
||||
ignore-regex = \b\S+…\S*|\b(doubleClick|afterAll|PostgresSQL)\b|\bWee, L\.|.*te.*Telugu.*
|
||||
# some odd variables
|
||||
ignore-words-list = datea,ser,childrens
|
||||
@@ -1,48 +0,0 @@
|
||||
worker_processes 1;
|
||||
|
||||
events { worker_connections 1024; }
|
||||
|
||||
http {
|
||||
|
||||
upstream orthanc-server {
|
||||
server orthanc:8042;
|
||||
}
|
||||
|
||||
server {
|
||||
listen [::]:80 default_server;
|
||||
listen 80;
|
||||
|
||||
# CORS Magic
|
||||
add_header 'Access-Control-Allow-Origin' '*';
|
||||
add_header 'Access-Control-Allow_Credentials' 'true';
|
||||
add_header 'Access-Control-Allow-Headers' 'Authorization,Accept,Origin,DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Content-Range,Range';
|
||||
add_header 'Access-Control-Allow-Methods' 'GET,POST,OPTIONS,PUT,DELETE,PATCH';
|
||||
|
||||
location / {
|
||||
|
||||
if ($request_method = 'OPTIONS') {
|
||||
add_header 'Access-Control-Allow-Origin' '*';
|
||||
add_header 'Access-Control-Allow_Credentials' 'true';
|
||||
add_header 'Access-Control-Allow-Headers' 'Authorization,Accept,Origin,DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Content-Range,Range';
|
||||
add_header 'Access-Control-Allow-Methods' 'GET,POST,OPTIONS,PUT,DELETE,PATCH';
|
||||
add_header 'Access-Control-Max-Age' 1728000;
|
||||
add_header 'Content-Type' 'text/plain charset=UTF-8';
|
||||
add_header 'Content-Length' 0;
|
||||
return 204;
|
||||
}
|
||||
|
||||
proxy_pass http://orthanc:8042;
|
||||
proxy_redirect off;
|
||||
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-Host $server_name;
|
||||
|
||||
# CORS Magic
|
||||
add_header 'Access-Control-Allow-Origin' '*';
|
||||
add_header 'Access-Control-Allow_Credentials' 'true';
|
||||
add_header 'Access-Control-Allow-Headers' 'Authorization,Accept,Origin,DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Content-Range,Range';
|
||||
add_header 'Access-Control-Allow-Methods' 'GET,POST,OPTIONS,PUT,DELETE,PATCH';
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,89 +0,0 @@
|
||||
{
|
||||
"Name": "Orthanc inside Docker",
|
||||
"StorageDirectory": "/var/lib/orthanc/db",
|
||||
"IndexDirectory": "/var/lib/orthanc/db",
|
||||
"StorageCompression": false,
|
||||
"MaximumStorageSize": 0,
|
||||
"MaximumPatientCount": 0,
|
||||
"LuaScripts": [],
|
||||
"Plugins": ["/usr/share/orthanc/plugins", "/usr/local/share/orthanc/plugins"],
|
||||
"ConcurrentJobs": 2,
|
||||
"HttpServerEnabled": true,
|
||||
"HttpPort": 8042,
|
||||
"HttpDescribeErrors": true,
|
||||
"HttpCompressionEnabled": true,
|
||||
"DicomServerEnabled": true,
|
||||
"DicomAet": "ORTHANC",
|
||||
"DicomCheckCalledAet": false,
|
||||
"DicomPort": 4242,
|
||||
"DefaultEncoding": "Latin1",
|
||||
"DeflatedTransferSyntaxAccepted": true,
|
||||
"JpegTransferSyntaxAccepted": true,
|
||||
"Jpeg2000TransferSyntaxAccepted": true,
|
||||
"JpegLosslessTransferSyntaxAccepted": true,
|
||||
"JpipTransferSyntaxAccepted": true,
|
||||
"Mpeg2TransferSyntaxAccepted": true,
|
||||
"RleTransferSyntaxAccepted": true,
|
||||
"UnknownSopClassAccepted": false,
|
||||
"DicomScpTimeout": 30,
|
||||
|
||||
"RemoteAccessAllowed": true,
|
||||
"SslEnabled": false,
|
||||
"SslCertificate": "certificate.pem",
|
||||
"AuthenticationEnabled": false,
|
||||
"RegisteredUsers": {
|
||||
"test": "test"
|
||||
},
|
||||
"DicomModalities": {},
|
||||
"DicomModalitiesInDatabase": false,
|
||||
"DicomAlwaysAllowEcho": true,
|
||||
"DicomAlwaysAllowStore": true,
|
||||
"DicomCheckModalityHost": false,
|
||||
"DicomScuTimeout": 10,
|
||||
"OrthancPeers": {},
|
||||
"OrthancPeersInDatabase": false,
|
||||
"HttpProxy": "",
|
||||
|
||||
"HttpVerbose": true,
|
||||
|
||||
"HttpTimeout": 10,
|
||||
"HttpsVerifyPeers": true,
|
||||
"HttpsCACertificates": "",
|
||||
"UserMetadata": {},
|
||||
"UserContentType": {},
|
||||
"StableAge": 60,
|
||||
"StrictAetComparison": false,
|
||||
"StoreMD5ForAttachments": true,
|
||||
"LimitFindResults": 0,
|
||||
"LimitFindInstances": 0,
|
||||
"LimitJobs": 10,
|
||||
"LogExportedResources": false,
|
||||
"KeepAlive": true,
|
||||
"TcpNoDelay": true,
|
||||
"HttpThreadsCount": 50,
|
||||
"StoreDicom": true,
|
||||
"DicomAssociationCloseDelay": 5,
|
||||
"QueryRetrieveSize": 10,
|
||||
"CaseSensitivePN": false,
|
||||
"LoadPrivateDictionary": true,
|
||||
"Dictionary": {},
|
||||
"SynchronousCMove": true,
|
||||
"JobsHistorySize": 10,
|
||||
"SaveJobs": true,
|
||||
"OverwriteInstances": false,
|
||||
"MediaArchiveSize": 1,
|
||||
"StorageAccessOnFind": "Always",
|
||||
"MetricsEnabled": true,
|
||||
|
||||
"DicomWeb": {
|
||||
"Enable": true,
|
||||
"Root": "/dicom-web/",
|
||||
"EnableWado": true,
|
||||
"WadoRoot": "/wado",
|
||||
"Host": "127.0.0.1",
|
||||
"Ssl": false,
|
||||
"StowMaxInstances": 10,
|
||||
"StowMaxSize": 10,
|
||||
"QidoCaseSensitive": false
|
||||
}
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
version: '3.5'
|
||||
|
||||
services:
|
||||
orthanc:
|
||||
image: jodogne/orthanc-plugins:1.11.0
|
||||
hostname: orthanc
|
||||
volumes:
|
||||
# Config
|
||||
- ./config/orthanc.json:/etc/orthanc/orthanc.json:ro
|
||||
# Persist data
|
||||
- ./volumes/orthanc-db/:/var/lib/orthanc/db/
|
||||
ports:
|
||||
- '4242:4242' # DICOM
|
||||
- '8042:8042' # Web
|
||||
restart: unless-stopped
|
||||
nginx:
|
||||
image: nginx:latest
|
||||
volumes:
|
||||
- ./config/nginx.conf:/etc/nginx/nginx.conf
|
||||
ports:
|
||||
- '80:80' #ngnix proxy
|
||||
depends_on:
|
||||
- orthanc
|
||||
@@ -1,2 +0,0 @@
|
||||
*
|
||||
!.gitignore
|
||||
@@ -64,7 +64,7 @@ body:
|
||||
attributes:
|
||||
label: 'Node version'
|
||||
description: 'Your Node.js version.'
|
||||
placeholder: 'e.g., 16.14.0'
|
||||
placeholder: 'e.g., 18.16.1'
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
|
||||
@@ -83,7 +83,7 @@ after the commits are squashed.
|
||||
#### Tested Environment
|
||||
|
||||
- [] OS: <!--[e.g. Windows 10, macOS 10.15.4]-->
|
||||
- [] Node version: <!--[e.g. 16.14.0]-->
|
||||
- [] Node version: <!--[e.g. 18.16.1]-->
|
||||
- [] Browser:
|
||||
<!--[e.g. Chrome 83.0.4103.116, Firefox 77.0.1, Safari 13.1.1]-->
|
||||
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
---
|
||||
name: Codespell
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [master]
|
||||
pull_request:
|
||||
branches: [master]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
codespell:
|
||||
name: Check for spelling errors
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
- name: Codespell
|
||||
uses: codespell-project/actions-codespell@v2
|
||||
@@ -42,3 +42,6 @@ platform/app/src/pluginImports.js
|
||||
/Viewers.iml
|
||||
platform/app/.recipes/Nginx-Dcm4Che/dcm4che/dcm4che-arc/*
|
||||
platform/app/.recipes/OpenResty-Orthanc/logs/*
|
||||
.vercel
|
||||
|
||||
.vs
|
||||
+1
-1
@@ -1 +1 @@
|
||||
16.14.0
|
||||
18.16.1
|
||||
+2
-1
@@ -7,5 +7,6 @@
|
||||
"semi": true,
|
||||
"singleQuote": true,
|
||||
"arrowParens": "avoid",
|
||||
"singleAttributePerLine": true
|
||||
"singleAttributePerLine": true,
|
||||
"endOfLine": "auto"
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
const stylusToJavaScript = {
|
||||
test: /\.styl$/,
|
||||
use: [
|
||||
{ loader: 'style-loader' }, // 3. Style nodes from JS Strings
|
||||
{ loader: 'css-loader' }, // 2. CSS to CommonJS
|
||||
{ loader: 'stylus-loader' }, // 1. Stylus to CSS
|
||||
],
|
||||
};
|
||||
|
||||
module.exports = stylusToJavaScript;
|
||||
+26
-15
@@ -18,6 +18,7 @@ const loadShadersRule = require('./rules/loadShaders.js');
|
||||
const loadWebWorkersRule = require('./rules/loadWebWorkers.js');
|
||||
const transpileJavaScriptRule = require('./rules/transpileJavaScript.js');
|
||||
const cssToJavaScript = require('./rules/cssToJavaScript.js');
|
||||
const stylusToJavaScript = require('./rules/stylusToJavaScript.js');
|
||||
|
||||
// ~~ ENV VARS
|
||||
const NODE_ENV = process.env.NODE_ENV;
|
||||
@@ -32,6 +33,27 @@ const COMMIT_HASH = fs.readFileSync(path.join(__dirname, '../commit.txt'), 'utf8
|
||||
//
|
||||
dotenv.config();
|
||||
|
||||
const defineValues = {
|
||||
/* Application */
|
||||
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV),
|
||||
'process.env.NODE_DEBUG': JSON.stringify(process.env.NODE_DEBUG),
|
||||
'process.env.DEBUG': JSON.stringify(process.env.DEBUG),
|
||||
'process.env.PUBLIC_URL': JSON.stringify(process.env.PUBLIC_URL || '/'),
|
||||
'process.env.BUILD_NUM': JSON.stringify(BUILD_NUM),
|
||||
'process.env.VERSION_NUMBER': JSON.stringify(VERSION_NUMBER),
|
||||
'process.env.COMMIT_HASH': JSON.stringify(COMMIT_HASH),
|
||||
/* i18n */
|
||||
'process.env.USE_LOCIZE': JSON.stringify(process.env.USE_LOCIZE || ''),
|
||||
'process.env.LOCIZE_PROJECTID': JSON.stringify(process.env.LOCIZE_PROJECTID || ''),
|
||||
'process.env.LOCIZE_API_KEY': JSON.stringify(process.env.LOCIZE_API_KEY || ''),
|
||||
'process.env.REACT_APP_I18N_DEBUG': JSON.stringify(process.env.REACT_APP_I18N_DEBUG || ''),
|
||||
};
|
||||
|
||||
// Only redefine updated values. This avoids warning messages in the logs
|
||||
if (!process.env.APP_CONFIG) {
|
||||
defineValues['process.env.APP_CONFIG'] = '';
|
||||
}
|
||||
|
||||
module.exports = (env, argv, { SRC_DIR, ENTRY }) => {
|
||||
if (!process.env.NODE_ENV) {
|
||||
throw new Error('process.env.NODE_ENV not set');
|
||||
@@ -91,6 +113,9 @@ module.exports = (env, argv, { SRC_DIR, ENTRY }) => {
|
||||
},
|
||||
},
|
||||
cssToJavaScript,
|
||||
// Note: Only uncomment the following if you are using the old style of stylus in v2
|
||||
// Also you need to uncomment this platform/app/.webpack/rules/extractStyleChunks.js
|
||||
// stylusToJavaScript,
|
||||
{
|
||||
test: /\.wasm/,
|
||||
type: 'asset/resource',
|
||||
@@ -133,21 +158,7 @@ module.exports = (env, argv, { SRC_DIR, ENTRY }) => {
|
||||
},
|
||||
},
|
||||
plugins: [
|
||||
new webpack.DefinePlugin({
|
||||
/* Application */
|
||||
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV),
|
||||
'process.env.DEBUG': JSON.stringify(process.env.DEBUG),
|
||||
'process.env.APP_CONFIG': JSON.stringify(process.env.APP_CONFIG || ''),
|
||||
'process.env.PUBLIC_URL': JSON.stringify(process.env.PUBLIC_URL || '/'),
|
||||
'process.env.BUILD_NUM': JSON.stringify(BUILD_NUM),
|
||||
'process.env.VERSION_NUMBER': JSON.stringify(VERSION_NUMBER),
|
||||
'process.env.COMMIT_HASH': JSON.stringify(COMMIT_HASH),
|
||||
/* i18n */
|
||||
'process.env.USE_LOCIZE': JSON.stringify(process.env.USE_LOCIZE || ''),
|
||||
'process.env.LOCIZE_PROJECTID': JSON.stringify(process.env.LOCIZE_PROJECTID || ''),
|
||||
'process.env.LOCIZE_API_KEY': JSON.stringify(process.env.LOCIZE_API_KEY || ''),
|
||||
'process.env.REACT_APP_I18N_DEBUG': JSON.stringify(process.env.REACT_APP_I18N_DEBUG || ''),
|
||||
}),
|
||||
new webpack.DefinePlugin(defineValues),
|
||||
new webpack.ProvidePlugin({
|
||||
Buffer: ['buffer', 'Buffer'],
|
||||
}),
|
||||
|
||||
+480
@@ -3,6 +3,486 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [3.7.0](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.110...v3.7.0) (2023-10-11)
|
||||
|
||||
|
||||
|
||||
# [3.6.0](https://github.com/OHIF/Viewers/compare/v3.6.0-beta.3...v3.6.0) (2023-06-07)
|
||||
|
||||
|
||||
|
||||
# [3.5.0](https://github.com/OHIF/Viewers/compare/v3.5.0-beta.1...v3.5.0) (2023-06-07)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **app:** change ohif viewer to ohif app ([#3451](https://github.com/OHIF/Viewers/issues/3451)) ([16afa74](https://github.com/OHIF/Viewers/commit/16afa740b60b31037100444ef9311b80ffea2f67))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.110](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.109...v3.7.0-beta.110) (2023-10-11)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **display messages:** broken after timings ([#3719](https://github.com/OHIF/Viewers/issues/3719)) ([157b88c](https://github.com/OHIF/Viewers/commit/157b88c909d3289cb89ace731c1f9a19d40797ac))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.109](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.108...v3.7.0-beta.109) (2023-10-11)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **export:** wrong export for the tmtv RT function ([#3715](https://github.com/OHIF/Viewers/issues/3715)) ([a3f2a1a](https://github.com/OHIF/Viewers/commit/a3f2a1a7b0d16bfcc0ecddc2ab731e54c5e377c8))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.108](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.107...v3.7.0-beta.108) (2023-10-10)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **i18n:** display set(s) are two words for English messages ([#3711](https://github.com/OHIF/Viewers/issues/3711)) ([c3a5847](https://github.com/OHIF/Viewers/commit/c3a5847dcd3dce4f1c8d8b11af95f79e3f93f70d))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.107](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.106...v3.7.0-beta.107) (2023-10-10)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **modules:** add stylus loader as an option to be uncommented ([#3710](https://github.com/OHIF/Viewers/issues/3710)) ([7c57f67](https://github.com/OHIF/Viewers/commit/7c57f67844b790fc6e47ac3f9708bf9d576389c8))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.106](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.105...v3.7.0-beta.106) (2023-10-10)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **segmentation:** Various fixes for segmentation mode and other ([#3709](https://github.com/OHIF/Viewers/issues/3709)) ([a9a6ad5](https://github.com/OHIF/Viewers/commit/a9a6ad50eae67b43b8b34efc07182d788cacdcfe))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.105](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.104...v3.7.0-beta.105) (2023-10-10)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **voi:** should publish voi change event on reset ([#3707](https://github.com/OHIF/Viewers/issues/3707)) ([52f34c6](https://github.com/OHIF/Viewers/commit/52f34c64d014f433ec1661a39b47e7fb27f15332))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.104](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.103...v3.7.0-beta.104) (2023-10-09)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **modality unit:** fix the modality unit per target via upgrade of cs3d ([#3706](https://github.com/OHIF/Viewers/issues/3706)) ([0a42d57](https://github.com/OHIF/Viewers/commit/0a42d573bbca7f2551a831a46d3aa6b56674a580))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.103](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.102...v3.7.0-beta.103) (2023-10-09)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **segmentation:** do not use SAB if not specified ([#3705](https://github.com/OHIF/Viewers/issues/3705)) ([4911e47](https://github.com/OHIF/Viewers/commit/4911e4796cef5e22cb7cc0ca73dc5c956bc75339))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.102](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.101...v3.7.0-beta.102) (2023-10-06)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **Segmentation:** download RTSS from Labelmap([#3692](https://github.com/OHIF/Viewers/issues/3692)) ([40673f6](https://github.com/OHIF/Viewers/commit/40673f64b36b1150149c55632aa1825178a39e65))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.101](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.100...v3.7.0-beta.101) (2023-10-06)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **bugs:** fixing lots of bugs regarding release candidate ([#3700](https://github.com/OHIF/Viewers/issues/3700)) ([8bc12a3](https://github.com/OHIF/Viewers/commit/8bc12a37d0353160ae5ea4624dc0b244b7d59c07))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.100](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.99...v3.7.0-beta.100) (2023-10-06)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **segmentation scroll:** and hydration bugs ([#3701](https://github.com/OHIF/Viewers/issues/3701)) ([1fd98d9](https://github.com/OHIF/Viewers/commit/1fd98d922094d10fe0c6e9df726314ec9fce49e8))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.99](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.98...v3.7.0-beta.99) (2023-10-04)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **measurement and microscopy:** various small fixes for measurement and microscopy side panel ([#3696](https://github.com/OHIF/Viewers/issues/3696)) ([c1d5ee7](https://github.com/OHIF/Viewers/commit/c1d5ee7e3f7f4c0c6bed9ae81eba5519741c5155))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.98](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.97...v3.7.0-beta.98) (2023-10-04)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **locale:** add German translations - community PR ([#3697](https://github.com/OHIF/Viewers/issues/3697)) ([ebe8f71](https://github.com/OHIF/Viewers/commit/ebe8f71da22c1d24b58f889c5d803951e19817b6))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.97](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.96...v3.7.0-beta.97) (2023-10-04)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **locale:** Added Turkish language support (tr-TR) - Community PR ([#3695](https://github.com/OHIF/Viewers/issues/3695)) ([745050a](https://github.com/OHIF/Viewers/commit/745050a28ec7c2ef2e9a4d4e590040050b2177b2))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.96](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.95...v3.7.0-beta.96) (2023-10-04)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **translation:** Side panel translate fix ([#3156](https://github.com/OHIF/Viewers/issues/3156)) ([29748d4](https://github.com/OHIF/Viewers/commit/29748d46a14d23817dbe196e0f64363fc61a8aed))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.95](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.94...v3.7.0-beta.95) (2023-10-04)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **cli:** Add npm packaged mode not working ([#3689](https://github.com/OHIF/Viewers/issues/3689)) ([28cec04](https://github.com/OHIF/Viewers/commit/28cec04ff43b81e218c3e9addef4665b3833a6fe))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.94](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.93...v3.7.0-beta.94) (2023-10-03)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **debug:** Add timing information about time to first image/all images, and query time ([#3681](https://github.com/OHIF/Viewers/issues/3681)) ([108383b](https://github.com/OHIF/Viewers/commit/108383b9ef51e4bef82d9c932b9bc7aa5354e799))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.93](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.92...v3.7.0-beta.93) (2023-10-03)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **displayArea:** add display area to hanging protocol ([#3691](https://github.com/OHIF/Viewers/issues/3691)) ([5e7fe91](https://github.com/OHIF/Viewers/commit/5e7fe91617d7399f85702d82e7bfa028b8010a89))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.92](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.91...v3.7.0-beta.92) (2023-10-03)
|
||||
|
||||
**Note:** Version bump only for package ohif-monorepo-root
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.91](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.90...v3.7.0-beta.91) (2023-10-03)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **editing:** regression bug in disable editing ([#3687](https://github.com/OHIF/Viewers/issues/3687)) ([4dc2acd](https://github.com/OHIF/Viewers/commit/4dc2acdefa872dd1d8df47f465e9e9656f95f67f))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.90](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.89...v3.7.0-beta.90) (2023-10-03)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **typescript error:** Change pubSubServiceInterface file type to typescript ([#3546](https://github.com/OHIF/Viewers/issues/3546)) ([eb22328](https://github.com/OHIF/Viewers/commit/eb22328fc05d06fc4411805e7a30f826659d796a))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.89](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.88...v3.7.0-beta.89) (2023-10-03)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **dicom overlay:** Handle special cases of ArrayBuffer for various DICOM overlay attributes. ([#3684](https://github.com/OHIF/Viewers/issues/3684)) ([e36a604](https://github.com/OHIF/Viewers/commit/e36a6043315e900eeb6ce183772c7f852f478e96))
|
||||
* **StackSync:** Miscellaneous fixes for stack image sync ([#3663](https://github.com/OHIF/Viewers/issues/3663)) ([8a335bd](https://github.com/OHIF/Viewers/commit/8a335bd03d14ba87d65d7468d93f74040aa828d9))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.88](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.87...v3.7.0-beta.88) (2023-10-03)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **config:** support more values for the useSharedArrayBuffer ([#3688](https://github.com/OHIF/Viewers/issues/3688)) ([1129c15](https://github.com/OHIF/Viewers/commit/1129c155d2c7d46c98a5df7c09879aa3d459fa7e))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.87](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.86...v3.7.0-beta.87) (2023-09-29)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **no sab:** should work when shared array buffer is not required ([#3686](https://github.com/OHIF/Viewers/issues/3686)) ([a67d72d](https://github.com/OHIF/Viewers/commit/a67d72de85238b369a18c010bf6d147daefc6df5))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.86](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.85...v3.7.0-beta.86) (2023-09-29)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **cli:** various fixes for adding custom modes and extensions ([#3683](https://github.com/OHIF/Viewers/issues/3683)) ([dc73b18](https://github.com/OHIF/Viewers/commit/dc73b187484da029a2664bb1302f30137c973b8c))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.85](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.84...v3.7.0-beta.85) (2023-09-26)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **toggleOneUp:** fixed one up for main tmtv layout ([#3677](https://github.com/OHIF/Viewers/issues/3677)) ([86f54d0](https://github.com/OHIF/Viewers/commit/86f54d0d07042750a863ae876aa8dd5fb16029a5))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.84](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.83...v3.7.0-beta.84) (2023-09-26)
|
||||
|
||||
**Note:** Version bump only for package ohif-monorepo-root
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.83](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.82...v3.7.0-beta.83) (2023-09-26)
|
||||
|
||||
**Note:** Version bump only for package ohif-monorepo-root
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.82](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.81...v3.7.0-beta.82) (2023-09-26)
|
||||
|
||||
**Note:** Version bump only for package ohif-monorepo-root
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.81](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.80...v3.7.0-beta.81) (2023-09-26)
|
||||
|
||||
**Note:** Version bump only for package ohif-monorepo-root
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.80](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.79...v3.7.0-beta.80) (2023-09-22)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **react-select:** update react select package ([#3622](https://github.com/OHIF/Viewers/issues/3622)) ([04ca10d](https://github.com/OHIF/Viewers/commit/04ca10d8779dd15454920002f3d48afa8830de8a))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **segmentation mode:** Add create, and export SEG with Brushes ([#3632](https://github.com/OHIF/Viewers/issues/3632)) ([48bbd62](https://github.com/OHIF/Viewers/commit/48bbd6281a497ea68670239f5426a10ee6c56dc1))
|
||||
* **SidePanel:** new side panel tab look-and-feel ([#3657](https://github.com/OHIF/Viewers/issues/3657)) ([85c899b](https://github.com/OHIF/Viewers/commit/85c899b399e2521480724be145538993721b9378))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.79](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.78...v3.7.0-beta.79) (2023-09-22)
|
||||
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* **memory:** add 16 bit texture via configuration - reduces memory by half ([#3662](https://github.com/OHIF/Viewers/issues/3662)) ([2bd3b26](https://github.com/OHIF/Viewers/commit/2bd3b26a6aa54b211ef988f3ad64ef1fe5648bab))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.78](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.77...v3.7.0-beta.78) (2023-09-21)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **mpr:** Return the original/raw hanging protocol when fetching and preserving the current active protocol. ([#3670](https://github.com/OHIF/Viewers/issues/3670)) ([221dedd](https://github.com/OHIF/Viewers/commit/221dedde5dd4df086276406a9fa2da1cc23b4eb1))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.77](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.76...v3.7.0-beta.77) (2023-09-21)
|
||||
|
||||
**Note:** Version bump only for package ohif-monorepo-root
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.76](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.75...v3.7.0-beta.76) (2023-09-19)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **keyCloak:** fix openresty keycloak deployment recipe ([#3655](https://github.com/OHIF/Viewers/issues/3655)) ([2d7721c](https://github.com/OHIF/Viewers/commit/2d7721cb581f55dc49e3baeca2411b18dd78ad74))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.75](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.74...v3.7.0-beta.75) (2023-09-18)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **DicomJson:** retrieve.series.metadata method should be async ([#3659](https://github.com/OHIF/Viewers/issues/3659)) ([2737903](https://github.com/OHIF/Viewers/commit/2737903386cf97399473e0fa64fe53ad14da155a))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.74](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.73...v3.7.0-beta.74) (2023-09-15)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **measurements:** Update the calibration tool to match changes in CS3D ([#3505](https://github.com/OHIF/Viewers/issues/3505)) ([38af311](https://github.com/OHIF/Viewers/commit/38af3112ec1f94f36c0ef64ff1cf9d21c0981c81))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.73](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.72...v3.7.0-beta.73) (2023-09-12)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **health imaging:** studies not loading from healthimaging if imagepositionpatient is missing ([#3646](https://github.com/OHIF/Viewers/issues/3646)) ([74e62a1](https://github.com/OHIF/Viewers/commit/74e62a176374f720080d4e777972f70e7f2d8b2b))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.72](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.71...v3.7.0-beta.72) (2023-09-12)
|
||||
|
||||
**Note:** Version bump only for package ohif-monorepo-root
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.71](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.70...v3.7.0-beta.71) (2023-09-12)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **suv:** import calculate-suv library version that prevents SUV calculation for a zero PatientWeight ([#3638](https://github.com/OHIF/Viewers/issues/3638)) ([0d10f46](https://github.com/OHIF/Viewers/commit/0d10f46b885fe54ec3dae1848134da658eb6280a))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.70](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.69...v3.7.0-beta.70) (2023-09-12)
|
||||
|
||||
**Note:** Version bump only for package ohif-monorepo-root
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.69](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.68...v3.7.0-beta.69) (2023-09-11)
|
||||
|
||||
**Note:** Version bump only for package ohif-monorepo-root
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.68](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.67...v3.7.0-beta.68) (2023-09-11)
|
||||
|
||||
**Note:** Version bump only for package ohif-monorepo-root
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.67](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.66...v3.7.0-beta.67) (2023-09-06)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **hotkeys:** preserve hotkeys if changed, and reduce re-rendering ([#3635](https://github.com/OHIF/Viewers/issues/3635)) ([94f7cfb](https://github.com/OHIF/Viewers/commit/94f7cfb08e3490488394efc42ef089ebe55e86be))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.66](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.65...v3.7.0-beta.66) (2023-09-06)
|
||||
|
||||
**Note:** Version bump only for package ohif-monorepo-root
|
||||
|
||||
+2
-2
@@ -21,7 +21,7 @@
|
||||
|
||||
# Stage 1: Build the application
|
||||
# docker build -t ohif/viewer:latest .
|
||||
FROM node:16.15.0-slim as json-copier
|
||||
FROM node:18.16.1-slim as json-copier
|
||||
|
||||
RUN mkdir /usr/src/app
|
||||
WORKDIR /usr/src/app
|
||||
@@ -37,7 +37,7 @@ COPY platform /usr/src/app/platform
|
||||
#RUN find platform \! -name "package.json" -mindepth 2 -maxdepth 2 -print | xargs rm -rf
|
||||
|
||||
# Copy Files
|
||||
FROM node:16.15.0-slim as builder
|
||||
FROM node:18.16.1-slim as builder
|
||||
RUN apt-get update && apt-get install -y build-essential python3
|
||||
RUN mkdir /usr/src/app
|
||||
WORKDIR /usr/src/app
|
||||
|
||||
@@ -17,6 +17,10 @@ provided by the <a href="https://ohif.org/">Open Health Imaging Foundation (OHIF
|
||||
<div align="center">
|
||||
📰 <a href="https://ohif.org/news/"><strong>Join OHIF Newsletter</strong></a> 📰
|
||||
</div>
|
||||
<div align="center">
|
||||
📰 <a href="https://ohif.org/news/"><strong>Join OHIF Newsletter</strong></a> 📰
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<hr />
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
dd6a8812d131cfa9ed7dc8ca6c4068762d4c8d07
|
||||
5ddf8a16027255d28dc01c1740099cf85bbcf458
|
||||
@@ -1,134 +0,0 @@
|
||||
import ImageSet from '@ohif/core/src/classes/ImageSet';
|
||||
import { IWebApiDataSource } from '@ohif/core';
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
export default {
|
||||
id: '@ohif/extension-*',
|
||||
|
||||
/**
|
||||
* LIFECYCLE HOOKS
|
||||
*/
|
||||
preRegistration() {},
|
||||
beforeExtInit() {},
|
||||
beforeExtDestroy() {},
|
||||
|
||||
/**
|
||||
* MODULES
|
||||
*/
|
||||
getCommandsModule,
|
||||
getContextModule,
|
||||
getDataSourcesModule,
|
||||
getLayoutTemplateModule,
|
||||
getPanelModule,
|
||||
getSopClassHandlerModule,
|
||||
getToolbarModule() {},
|
||||
getViewportModule,
|
||||
};
|
||||
|
||||
// appConfig,
|
||||
// extensionConfig,
|
||||
// dataSources,
|
||||
// servicesManager,
|
||||
// extensionManager,
|
||||
// commandsManager,
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
const getCommandsModule = () => ({
|
||||
definitions: {
|
||||
exampleActionDef: {
|
||||
commandFn: ({ param1 }) => {
|
||||
console.log(`param1's value is: ${param1}`);
|
||||
},
|
||||
// storeContexts: ['viewports'],
|
||||
options: { param1: 'hello world' },
|
||||
context: 'VIEWER', // optional
|
||||
},
|
||||
},
|
||||
defaultContext: 'ACTIVE_VIEWPORT::DICOMSR',
|
||||
});
|
||||
|
||||
const ExampleContext = React.createContext();
|
||||
|
||||
function ExampleContextProvider({ children }) {
|
||||
return <ExampleContext.Provider value={{ example: 'value' }}>{children}</ExampleContext.Provider>;
|
||||
}
|
||||
|
||||
const getContextModule = () => [
|
||||
{
|
||||
name: 'ExampleContext',
|
||||
context: ExampleContext,
|
||||
provider: ExampleContextProvider,
|
||||
},
|
||||
];
|
||||
|
||||
const getDataSourcesModule = () => [
|
||||
{
|
||||
name: 'exampleDataSource',
|
||||
type: 'webApi', // 'webApi' | 'local' | 'other'
|
||||
createDataSource: dataSourceConfig => {
|
||||
return IWebApiDataSource.create(/* */);
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const getLayoutTemplateModule = (/* ... */) => [
|
||||
{
|
||||
id: 'exampleLayout',
|
||||
name: 'exampleLayout',
|
||||
component: ExampleLayoutComponent,
|
||||
},
|
||||
];
|
||||
|
||||
const getPanelModule = () => {
|
||||
return [
|
||||
{
|
||||
name: 'exampleSidePanel',
|
||||
iconName: 'info-circle-o',
|
||||
iconLabel: 'Example',
|
||||
label: 'Hello World',
|
||||
isDisabled: studies => {}, // optional
|
||||
component: ExamplePanelContentComponent,
|
||||
},
|
||||
];
|
||||
};
|
||||
|
||||
const getSopClassHandlerModule = (/* ... */) => {
|
||||
const BASIC_TEXT_SR = '1.2.840.10008.5.1.4.1.1.88.11';
|
||||
|
||||
return [
|
||||
{
|
||||
name: 'ExampleSopClassHandle',
|
||||
sopClassUids: [BASIC_TEXT_SR],
|
||||
getDisplaySetsFromSeries: instances => {
|
||||
const imageSet = new ImageSet(instances);
|
||||
|
||||
imageSet.setAttributes(/** */);
|
||||
imageSet.sortBy((a, b) => 0);
|
||||
|
||||
return imageSet;
|
||||
},
|
||||
},
|
||||
];
|
||||
};
|
||||
|
||||
const getToolbarModule = () => {};
|
||||
|
||||
// displaySet, dataSource
|
||||
const getViewportModule = () => {
|
||||
const wrappedViewport = props => {
|
||||
return (
|
||||
<ExampleViewport
|
||||
{...props}
|
||||
onEvent={data => {
|
||||
commandsManager.runCommand('commandName', data);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
return [{ name: 'example', component: wrappedViewport }];
|
||||
};
|
||||
@@ -3,6 +3,369 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [3.7.0](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.110...v3.7.0) (2023-10-11)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.110](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.109...v3.7.0-beta.110) (2023-10-11)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.109](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.108...v3.7.0-beta.109) (2023-10-11)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.108](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.107...v3.7.0-beta.108) (2023-10-10)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.107](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.106...v3.7.0-beta.107) (2023-10-10)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.106](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.105...v3.7.0-beta.106) (2023-10-10)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.105](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.104...v3.7.0-beta.105) (2023-10-10)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.104](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.103...v3.7.0-beta.104) (2023-10-09)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.103](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.102...v3.7.0-beta.103) (2023-10-09)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.102](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.101...v3.7.0-beta.102) (2023-10-06)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.101](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.100...v3.7.0-beta.101) (2023-10-06)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.100](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.99...v3.7.0-beta.100) (2023-10-06)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.99](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.98...v3.7.0-beta.99) (2023-10-04)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.98](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.97...v3.7.0-beta.98) (2023-10-04)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.97](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.96...v3.7.0-beta.97) (2023-10-04)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.96](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.95...v3.7.0-beta.96) (2023-10-04)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.95](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.94...v3.7.0-beta.95) (2023-10-04)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.94](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.93...v3.7.0-beta.94) (2023-10-03)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.93](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.92...v3.7.0-beta.93) (2023-10-03)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.92](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.91...v3.7.0-beta.92) (2023-10-03)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.91](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.90...v3.7.0-beta.91) (2023-10-03)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.90](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.89...v3.7.0-beta.90) (2023-10-03)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.89](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.88...v3.7.0-beta.89) (2023-10-03)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.88](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.87...v3.7.0-beta.88) (2023-10-03)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.87](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.86...v3.7.0-beta.87) (2023-09-29)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.86](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.85...v3.7.0-beta.86) (2023-09-29)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.85](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.84...v3.7.0-beta.85) (2023-09-26)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.84](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.83...v3.7.0-beta.84) (2023-09-26)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.83](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.82...v3.7.0-beta.83) (2023-09-26)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.82](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.81...v3.7.0-beta.82) (2023-09-26)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.81](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.80...v3.7.0-beta.81) (2023-09-26)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.80](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.79...v3.7.0-beta.80) (2023-09-22)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **segmentation mode:** Add create, and export SEG with Brushes ([#3632](https://github.com/OHIF/Viewers/issues/3632)) ([48bbd62](https://github.com/OHIF/Viewers/commit/48bbd6281a497ea68670239f5426a10ee6c56dc1))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.79](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.78...v3.7.0-beta.79) (2023-09-22)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.78](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.77...v3.7.0-beta.78) (2023-09-21)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.77](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.76...v3.7.0-beta.77) (2023-09-21)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.76](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.75...v3.7.0-beta.76) (2023-09-19)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.75](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.74...v3.7.0-beta.75) (2023-09-18)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.74](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.73...v3.7.0-beta.74) (2023-09-15)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.73](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.72...v3.7.0-beta.73) (2023-09-12)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.72](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.71...v3.7.0-beta.72) (2023-09-12)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.71](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.70...v3.7.0-beta.71) (2023-09-12)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.70](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.69...v3.7.0-beta.70) (2023-09-12)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.69](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.68...v3.7.0-beta.69) (2023-09-11)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.68](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.67...v3.7.0-beta.68) (2023-09-11)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.67](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.66...v3.7.0-beta.67) (2023-09-06)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.66](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.65...v3.7.0-beta.66) (2023-09-06)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@ohif/extension-cornerstone-dicom-rt",
|
||||
"version": "3.7.0-beta.66",
|
||||
"version": "3.7.0",
|
||||
"description": "DICOM RT read workflow",
|
||||
"author": "OHIF",
|
||||
"license": "MIT",
|
||||
@@ -31,10 +31,10 @@
|
||||
"start": "yarn run dev"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@ohif/core": "3.7.0-beta.66",
|
||||
"@ohif/extension-cornerstone": "3.7.0-beta.66",
|
||||
"@ohif/extension-default": "3.7.0-beta.66",
|
||||
"@ohif/i18n": "3.7.0-beta.66",
|
||||
"@ohif/core": "3.7.0",
|
||||
"@ohif/extension-cornerstone": "3.7.0",
|
||||
"@ohif/extension-default": "3.7.0",
|
||||
"@ohif/i18n": "3.7.0",
|
||||
"prop-types": "^15.6.2",
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2",
|
||||
|
||||
@@ -2,7 +2,6 @@ import { id } from './id';
|
||||
import React from 'react';
|
||||
import { Types } from '@ohif/core';
|
||||
import getSopClassHandlerModule from './getSopClassHandlerModule';
|
||||
import hydrateRTDisplaySet from './utils/_hydrateRT';
|
||||
|
||||
const Component = React.lazy(() => {
|
||||
return import(/* webpackPrefetch: true */ './viewports/OHIFCornerstoneRTViewport');
|
||||
@@ -60,4 +59,3 @@ const extension: Types.Extensions.Extension = {
|
||||
};
|
||||
|
||||
export default extension;
|
||||
export { hydrateRTDisplaySet };
|
||||
@@ -1,70 +0,0 @@
|
||||
async function _hydrateRTDisplaySet({ rtDisplaySet, viewportId, servicesManager }) {
|
||||
const { segmentationService, hangingProtocolService, viewportGridService } =
|
||||
servicesManager.services;
|
||||
|
||||
const displaySetInstanceUID = rtDisplaySet.referencedDisplaySetInstanceUID;
|
||||
|
||||
let segmentationId = null;
|
||||
|
||||
// We need the hydration to notify panels about the new segmentation added
|
||||
const suppressEvents = false;
|
||||
|
||||
segmentationId = await segmentationService.createSegmentationForRTDisplaySet(
|
||||
rtDisplaySet,
|
||||
segmentationId,
|
||||
suppressEvents
|
||||
);
|
||||
|
||||
segmentationService.hydrateSegmentation(rtDisplaySet.displaySetInstanceUID);
|
||||
|
||||
const { viewports } = viewportGridService.getState();
|
||||
|
||||
const updatedViewports = hangingProtocolService.getViewportsRequireUpdate(
|
||||
viewportId,
|
||||
displaySetInstanceUID
|
||||
);
|
||||
|
||||
viewportGridService.setDisplaySetsForViewports(updatedViewports);
|
||||
|
||||
// Todo: fix this after we have a better way for stack viewport segmentations
|
||||
|
||||
// check every viewport in the viewports to see if the displaySetInstanceUID
|
||||
// is being displayed, if so we need to update the viewport to use volume viewport
|
||||
// (if already is not using it) since Cornerstone3D currently only supports
|
||||
// volume viewport for segmentation
|
||||
viewports.forEach(viewport => {
|
||||
if (viewport.viewportId === viewportId) {
|
||||
return;
|
||||
}
|
||||
|
||||
const shouldDisplaySeg = segmentationService.shouldRenderSegmentation(
|
||||
viewport.displaySetInstanceUIDs,
|
||||
rtDisplaySet.displaySetInstanceUID
|
||||
);
|
||||
|
||||
if (shouldDisplaySeg) {
|
||||
updatedViewports.push({
|
||||
viewportId: viewport.viewportId,
|
||||
displaySetInstanceUIDs: viewport.displaySetInstanceUIDs,
|
||||
viewportOptions: {
|
||||
// Note: This is a hack to get the grid to re-render the OHIFCornerstoneViewport component
|
||||
// Used for segmentation hydration right now, since the logic to decide whether
|
||||
// a viewport needs to render a segmentation lives inside the CornerstoneViewportService
|
||||
// so we need to re-render (force update via change of the needsRerendering) so that React
|
||||
// does the diffing and decides we should render this again (although the id and element has not changed)
|
||||
// so that the CornerstoneViewportService can decide whether to render the segmentation or not.
|
||||
needsRerendering: true,
|
||||
initialImageOptions: {
|
||||
preset: 'middle',
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Do the entire update at once
|
||||
viewportGridService.setDisplaySetsForViewports(updatedViewports);
|
||||
return true;
|
||||
}
|
||||
|
||||
export default _hydrateRTDisplaySet;
|
||||
@@ -1,7 +1,7 @@
|
||||
function createRTToolGroupAndAddTools(ToolGroupService, customizationService, toolGroupId) {
|
||||
const { tools } = customizationService.get('cornerstone.overlayViewportTools') ?? {};
|
||||
|
||||
return ToolGroupService.createToolGroupAndAddTools(toolGroupId, tools, {});
|
||||
return ToolGroupService.createToolGroupAndAddTools(toolGroupId, tools);
|
||||
}
|
||||
|
||||
export default createRTToolGroupAndAddTools;
|
||||
@@ -1,5 +1,4 @@
|
||||
import { ButtonEnums } from '@ohif/ui';
|
||||
import hydrateRTDisplaySet from './_hydrateRT';
|
||||
|
||||
const RESPONSE = {
|
||||
NO_NEVER: -1,
|
||||
@@ -13,6 +12,7 @@ function promptHydrateRT({
|
||||
viewportId,
|
||||
toolGroupId = 'default',
|
||||
preHydrateCallbacks,
|
||||
hydrateRTDisplaySet,
|
||||
}) {
|
||||
const { uiViewportDialogService } = servicesManager.services;
|
||||
|
||||
|
||||
@@ -3,11 +3,9 @@ import PropTypes from 'prop-types';
|
||||
import OHIF, { utils } from '@ohif/core';
|
||||
import { ViewportActionBar, useViewportGrid, LoadingIndicatorTotalPercent } from '@ohif/ui';
|
||||
|
||||
import _hydrateRTdisplaySet from '../utils/_hydrateRT';
|
||||
import promptHydrateRT from '../utils/promptHydrateRT';
|
||||
import _getStatusComponent from './_getStatusComponent';
|
||||
import createRTToolGroupAndAddTools from '../utils/initRTToolGroup';
|
||||
import _hydrateRTDisplaySet from '../utils/_hydrateRT';
|
||||
|
||||
const { formatDate } = utils;
|
||||
const RT_TOOLGROUP_BASE_NAME = 'RTToolGroup';
|
||||
@@ -95,6 +93,13 @@ function OHIFCornerstoneRTViewport(props) {
|
||||
});
|
||||
}, [viewportGrid]);
|
||||
|
||||
const hydrateRTDisplaySet = ({ rtDisplaySet, viewportId }) => {
|
||||
commandsManager.runCommand('loadSegmentationDisplaySetsForViewport', {
|
||||
displaySets: [rtDisplaySet],
|
||||
viewportId,
|
||||
});
|
||||
};
|
||||
|
||||
const getCornerstoneViewport = useCallback(() => {
|
||||
const { component: Component } = extensionManager.getModuleEntry(
|
||||
'@ohif/extension-cornerstone.viewportModule.cornerstone'
|
||||
@@ -154,6 +159,7 @@ function OHIFCornerstoneRTViewport(props) {
|
||||
viewportId,
|
||||
rtDisplaySet,
|
||||
preHydrateCallbacks: [storePresentationState],
|
||||
hydrateRTDisplaySet,
|
||||
}).then(isHydrated => {
|
||||
if (isHydrated) {
|
||||
setIsHydrated(true);
|
||||
@@ -295,10 +301,9 @@ function OHIFCornerstoneRTViewport(props) {
|
||||
// presentation state (w/l and invert) and then opens the RT. If we don't store
|
||||
// the presentation state, the viewport will be reset to the default presentation
|
||||
storePresentationState();
|
||||
const isHydrated = await _hydrateRTDisplaySet({
|
||||
const isHydrated = await hydrateRTDisplaySet({
|
||||
rtDisplaySet,
|
||||
viewportId,
|
||||
servicesManager,
|
||||
});
|
||||
|
||||
setIsHydrated(isHydrated);
|
||||
|
||||
@@ -45,10 +45,10 @@ module.exports = (env, argv) => {
|
||||
new webpack.optimize.LimitChunkCountPlugin({
|
||||
maxChunks: 1,
|
||||
}),
|
||||
// new MiniCssExtractPlugin({
|
||||
// filename: `./dist/${outputName}.css`,
|
||||
// chunkFilename: `./dist/${outputName}.css`,
|
||||
// }),
|
||||
new MiniCssExtractPlugin({
|
||||
filename: `./dist/${outputName}.css`,
|
||||
chunkFilename: `./dist/${outputName}.css`,
|
||||
}),
|
||||
],
|
||||
});
|
||||
};
|
||||
@@ -3,6 +3,388 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [3.7.0](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.110...v3.7.0) (2023-10-11)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-seg
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.110](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.109...v3.7.0-beta.110) (2023-10-11)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-seg
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.109](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.108...v3.7.0-beta.109) (2023-10-11)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-seg
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.108](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.107...v3.7.0-beta.108) (2023-10-10)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-seg
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.107](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.106...v3.7.0-beta.107) (2023-10-10)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-seg
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.106](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.105...v3.7.0-beta.106) (2023-10-10)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-seg
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.105](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.104...v3.7.0-beta.105) (2023-10-10)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **voi:** should publish voi change event on reset ([#3707](https://github.com/OHIF/Viewers/issues/3707)) ([52f34c6](https://github.com/OHIF/Viewers/commit/52f34c64d014f433ec1661a39b47e7fb27f15332))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.104](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.103...v3.7.0-beta.104) (2023-10-09)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **modality unit:** fix the modality unit per target via upgrade of cs3d ([#3706](https://github.com/OHIF/Viewers/issues/3706)) ([0a42d57](https://github.com/OHIF/Viewers/commit/0a42d573bbca7f2551a831a46d3aa6b56674a580))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.103](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.102...v3.7.0-beta.103) (2023-10-09)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-seg
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.102](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.101...v3.7.0-beta.102) (2023-10-06)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **Segmentation:** download RTSS from Labelmap([#3692](https://github.com/OHIF/Viewers/issues/3692)) ([40673f6](https://github.com/OHIF/Viewers/commit/40673f64b36b1150149c55632aa1825178a39e65))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.101](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.100...v3.7.0-beta.101) (2023-10-06)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-seg
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.100](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.99...v3.7.0-beta.100) (2023-10-06)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **segmentation scroll:** and hydration bugs ([#3701](https://github.com/OHIF/Viewers/issues/3701)) ([1fd98d9](https://github.com/OHIF/Viewers/commit/1fd98d922094d10fe0c6e9df726314ec9fce49e8))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.99](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.98...v3.7.0-beta.99) (2023-10-04)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **measurement and microscopy:** various small fixes for measurement and microscopy side panel ([#3696](https://github.com/OHIF/Viewers/issues/3696)) ([c1d5ee7](https://github.com/OHIF/Viewers/commit/c1d5ee7e3f7f4c0c6bed9ae81eba5519741c5155))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.98](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.97...v3.7.0-beta.98) (2023-10-04)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-seg
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.97](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.96...v3.7.0-beta.97) (2023-10-04)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-seg
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.96](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.95...v3.7.0-beta.96) (2023-10-04)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-seg
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.95](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.94...v3.7.0-beta.95) (2023-10-04)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-seg
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.94](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.93...v3.7.0-beta.94) (2023-10-03)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-seg
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.93](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.92...v3.7.0-beta.93) (2023-10-03)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-seg
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.92](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.91...v3.7.0-beta.92) (2023-10-03)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-seg
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.91](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.90...v3.7.0-beta.91) (2023-10-03)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-seg
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.90](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.89...v3.7.0-beta.90) (2023-10-03)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-seg
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.89](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.88...v3.7.0-beta.89) (2023-10-03)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-seg
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.88](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.87...v3.7.0-beta.88) (2023-10-03)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **config:** support more values for the useSharedArrayBuffer ([#3688](https://github.com/OHIF/Viewers/issues/3688)) ([1129c15](https://github.com/OHIF/Viewers/commit/1129c155d2c7d46c98a5df7c09879aa3d459fa7e))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.87](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.86...v3.7.0-beta.87) (2023-09-29)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-seg
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.86](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.85...v3.7.0-beta.86) (2023-09-29)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-seg
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.85](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.84...v3.7.0-beta.85) (2023-09-26)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-seg
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.84](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.83...v3.7.0-beta.84) (2023-09-26)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-seg
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.83](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.82...v3.7.0-beta.83) (2023-09-26)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-seg
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.82](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.81...v3.7.0-beta.82) (2023-09-26)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-seg
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.81](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.80...v3.7.0-beta.81) (2023-09-26)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-seg
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.80](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.79...v3.7.0-beta.80) (2023-09-22)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **segmentation mode:** Add create, and export SEG with Brushes ([#3632](https://github.com/OHIF/Viewers/issues/3632)) ([48bbd62](https://github.com/OHIF/Viewers/commit/48bbd6281a497ea68670239f5426a10ee6c56dc1))
|
||||
* **SidePanel:** new side panel tab look-and-feel ([#3657](https://github.com/OHIF/Viewers/issues/3657)) ([85c899b](https://github.com/OHIF/Viewers/commit/85c899b399e2521480724be145538993721b9378))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.79](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.78...v3.7.0-beta.79) (2023-09-22)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-seg
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.78](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.77...v3.7.0-beta.78) (2023-09-21)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-seg
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.77](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.76...v3.7.0-beta.77) (2023-09-21)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-seg
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.76](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.75...v3.7.0-beta.76) (2023-09-19)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-seg
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.75](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.74...v3.7.0-beta.75) (2023-09-18)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-seg
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.74](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.73...v3.7.0-beta.74) (2023-09-15)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-seg
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.73](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.72...v3.7.0-beta.73) (2023-09-12)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-seg
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.72](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.71...v3.7.0-beta.72) (2023-09-12)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-seg
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.71](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.70...v3.7.0-beta.71) (2023-09-12)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-seg
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.70](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.69...v3.7.0-beta.70) (2023-09-12)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-seg
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.69](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.68...v3.7.0-beta.69) (2023-09-11)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-seg
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.68](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.67...v3.7.0-beta.68) (2023-09-11)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-seg
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.67](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.66...v3.7.0-beta.67) (2023-09-06)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-seg
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.66](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.65...v3.7.0-beta.66) (2023-09-06)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-seg
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@ohif/extension-cornerstone-dicom-seg",
|
||||
"version": "3.7.0-beta.66",
|
||||
"version": "3.7.0",
|
||||
"description": "DICOM SEG read workflow",
|
||||
"author": "OHIF",
|
||||
"license": "MIT",
|
||||
@@ -31,10 +31,10 @@
|
||||
"start": "yarn run dev"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@ohif/core": "3.7.0-beta.66",
|
||||
"@ohif/extension-cornerstone": "3.7.0-beta.66",
|
||||
"@ohif/extension-default": "3.7.0-beta.66",
|
||||
"@ohif/i18n": "3.7.0-beta.66",
|
||||
"@ohif/core": "3.7.0",
|
||||
"@ohif/extension-cornerstone": "3.7.0",
|
||||
"@ohif/extension-default": "3.7.0",
|
||||
"@ohif/i18n": "3.7.0",
|
||||
"prop-types": "^15.6.2",
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2",
|
||||
@@ -44,6 +44,9 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.20.13",
|
||||
"@cornerstonejs/adapters": "^1.20.3",
|
||||
"@cornerstonejs/tools": "^1.20.3",
|
||||
"@kitware/vtk.js": "27.3.1",
|
||||
"react-color": "^2.19.3"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,435 @@
|
||||
import dcmjs from 'dcmjs';
|
||||
import { createReportDialogPrompt } from '@ohif/extension-default';
|
||||
import { ServicesManager, Types } from '@ohif/core';
|
||||
import { cache, metaData } from '@cornerstonejs/core';
|
||||
import {
|
||||
segmentation as cornerstoneToolsSegmentation,
|
||||
Enums as cornerstoneToolsEnums,
|
||||
} from '@cornerstonejs/tools';
|
||||
import { adaptersRT, helpers, adaptersSEG } from '@cornerstonejs/adapters';
|
||||
import { classes, DicomMetadataStore } from '@ohif/core';
|
||||
|
||||
import vtkImageMarchingSquares from '@kitware/vtk.js/Filters/General/ImageMarchingSquares';
|
||||
import vtkDataArray from '@kitware/vtk.js/Common/Core/DataArray';
|
||||
import vtkImageData from '@kitware/vtk.js/Common/DataModel/ImageData';
|
||||
|
||||
import {
|
||||
updateViewportsForSegmentationRendering,
|
||||
getUpdatedViewportsForSegmentation,
|
||||
getTargetViewport,
|
||||
} from './utils/hydrationUtils';
|
||||
|
||||
const { datasetToBlob } = dcmjs.data;
|
||||
|
||||
const {
|
||||
Cornerstone3D: {
|
||||
Segmentation: { generateLabelMaps2DFrom3D, generateSegmentation },
|
||||
},
|
||||
} = adaptersSEG;
|
||||
|
||||
const {
|
||||
Cornerstone3D: {
|
||||
RTSS: { generateRTSSFromSegmentations },
|
||||
},
|
||||
} = adaptersRT;
|
||||
|
||||
const { downloadDICOMData } = helpers;
|
||||
|
||||
const commandsModule = ({
|
||||
servicesManager,
|
||||
extensionManager,
|
||||
}: Types.Extensions.ExtensionParams): Types.Extensions.CommandsModule => {
|
||||
const {
|
||||
uiNotificationService,
|
||||
segmentationService,
|
||||
uiDialogService,
|
||||
displaySetService,
|
||||
viewportGridService,
|
||||
} = (servicesManager as ServicesManager).services;
|
||||
|
||||
const actions = {
|
||||
/**
|
||||
* Retrieves a list of viewports that require updates in preparation for segmentation rendering.
|
||||
* This function evaluates viewports based on their compatibility with the provided segmentation's
|
||||
* frame of reference UID and appends them to the updated list if they should render the segmentation.
|
||||
*
|
||||
* @param {Object} params - Parameters for the function.
|
||||
* @param params.viewportId - the ID of the viewport to be updated.
|
||||
* @param params.servicesManager - The services manager
|
||||
* @param params.referencedDisplaySetInstanceUID - Optional UID for the referenced display set instance.
|
||||
*
|
||||
* @returns {Array} Returns an array of viewports that require updates for segmentation rendering.
|
||||
*/
|
||||
getUpdatedViewportsForSegmentation,
|
||||
/**
|
||||
* Creates an empty segmentation for a specified viewport.
|
||||
* It first checks if the display set associated with the viewport is reconstructable.
|
||||
* If not, it raises a notification error. Otherwise, it creates a new segmentation
|
||||
* for the display set after handling the necessary steps for making the viewport
|
||||
* a volume viewport first
|
||||
*
|
||||
* @param {Object} params - Parameters for the function.
|
||||
* @param params.viewportId - the target viewport ID.
|
||||
*
|
||||
*/
|
||||
createEmptySegmentationForViewport: async ({ viewportId }) => {
|
||||
const viewport = getTargetViewport({ viewportId, viewportGridService });
|
||||
// Todo: add support for multiple display sets
|
||||
const displaySetInstanceUID = viewport.displaySetInstanceUIDs[0];
|
||||
|
||||
const displaySet = displaySetService.getDisplaySetByUID(displaySetInstanceUID);
|
||||
|
||||
if (!displaySet.isReconstructable) {
|
||||
uiNotificationService.show({
|
||||
title: 'Segmentation',
|
||||
message: 'Segmentation is not supported for non-reconstructible displaysets yet',
|
||||
type: 'error',
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
updateViewportsForSegmentationRendering({
|
||||
viewportId,
|
||||
servicesManager,
|
||||
loadFn: async () => {
|
||||
const currentSegmentations = segmentationService.getSegmentations();
|
||||
const segmentationId = await segmentationService.createSegmentationForDisplaySet(
|
||||
displaySetInstanceUID,
|
||||
{ label: `Segmentation ${currentSegmentations.length + 1}` }
|
||||
);
|
||||
|
||||
const toolGroupId = viewport.viewportOptions.toolGroupId;
|
||||
|
||||
await segmentationService.addSegmentationRepresentationToToolGroup(
|
||||
toolGroupId,
|
||||
segmentationId
|
||||
);
|
||||
|
||||
// Add only one segment for now
|
||||
segmentationService.addSegment(segmentationId, {
|
||||
toolGroupId,
|
||||
segmentIndex: 1,
|
||||
properties: {
|
||||
label: 'Segment 1',
|
||||
},
|
||||
});
|
||||
|
||||
return segmentationId;
|
||||
},
|
||||
});
|
||||
},
|
||||
/**
|
||||
* Loads segmentations for a specified viewport.
|
||||
* The function prepares the viewport for rendering, then loads the segmentation details.
|
||||
* Additionally, if the segmentation has scalar data, it is set for the corresponding label map volume.
|
||||
*
|
||||
* @param {Object} params - Parameters for the function.
|
||||
* @param params.segmentations - Array of segmentations to be loaded.
|
||||
* @param params.viewportId - the target viewport ID.
|
||||
*
|
||||
*/
|
||||
loadSegmentationsForViewport: async ({ segmentations, viewportId }) => {
|
||||
updateViewportsForSegmentationRendering({
|
||||
viewportId,
|
||||
servicesManager,
|
||||
loadFn: async () => {
|
||||
// Todo: handle adding more than one segmentation
|
||||
const viewport = getTargetViewport({ viewportId, viewportGridService });
|
||||
const displaySetInstanceUID = viewport.displaySetInstanceUIDs[0];
|
||||
|
||||
const segmentation = segmentations[0];
|
||||
const segmentationId = segmentation.id;
|
||||
const label = segmentation.label;
|
||||
const segments = segmentation.segments;
|
||||
|
||||
delete segmentation.segments;
|
||||
|
||||
await segmentationService.createSegmentationForDisplaySet(displaySetInstanceUID, {
|
||||
segmentationId,
|
||||
label,
|
||||
});
|
||||
|
||||
if (segmentation.scalarData) {
|
||||
const labelmapVolume = segmentationService.getLabelmapVolume(segmentationId);
|
||||
labelmapVolume.scalarData.set(segmentation.scalarData);
|
||||
}
|
||||
|
||||
segmentationService.addOrUpdateSegmentation(segmentation);
|
||||
|
||||
const toolGroupId = viewport.viewportOptions.toolGroupId;
|
||||
await segmentationService.addSegmentationRepresentationToToolGroup(
|
||||
toolGroupId,
|
||||
segmentationId
|
||||
);
|
||||
|
||||
segments.forEach(segment => {
|
||||
if (segment === null) {
|
||||
return;
|
||||
}
|
||||
segmentationService.addSegment(segmentationId, {
|
||||
segmentIndex: segment.segmentIndex,
|
||||
toolGroupId,
|
||||
properties: {
|
||||
color: segment.color,
|
||||
label: segment.label,
|
||||
opacity: segment.opacity,
|
||||
isLocked: segment.isLocked,
|
||||
visibility: segment.isVisible,
|
||||
active: segmentation.activeSegmentIndex === segment.segmentIndex,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
if (segmentation.centroidsIJK) {
|
||||
segmentationService.setCentroids(segmentation.id, segmentation.centroidsIJK);
|
||||
}
|
||||
|
||||
return segmentationId;
|
||||
},
|
||||
});
|
||||
},
|
||||
/**
|
||||
* Loads segmentation display sets for a specified viewport.
|
||||
* Depending on the modality of the display set (SEG or RTSTRUCT),
|
||||
* it chooses the appropriate service function to create
|
||||
* the segmentation for the display set.
|
||||
* The function then prepares the viewport for rendering segmentation.
|
||||
*
|
||||
* @param {Object} params - Parameters for the function.
|
||||
* @param params.viewportId - ID of the viewport where the segmentation display sets should be loaded.
|
||||
* @param params.displaySets - Array of display sets to be loaded for segmentation.
|
||||
*
|
||||
*/
|
||||
loadSegmentationDisplaySetsForViewport: async ({ viewportId, displaySets }) => {
|
||||
// Todo: handle adding more than one segmentation
|
||||
const displaySet = displaySets[0];
|
||||
|
||||
updateViewportsForSegmentationRendering({
|
||||
viewportId,
|
||||
servicesManager,
|
||||
referencedDisplaySetInstanceUID: displaySet.referencedDisplaySetInstanceUID,
|
||||
loadFn: async () => {
|
||||
const segDisplaySet = displaySet;
|
||||
const suppressEvents = false;
|
||||
const serviceFunction =
|
||||
segDisplaySet.Modality === 'SEG'
|
||||
? 'createSegmentationForSEGDisplaySet'
|
||||
: 'createSegmentationForRTDisplaySet';
|
||||
|
||||
const boundFn = segmentationService[serviceFunction].bind(segmentationService);
|
||||
const segmentationId = await boundFn(segDisplaySet, null, suppressEvents);
|
||||
|
||||
return segmentationId;
|
||||
},
|
||||
});
|
||||
},
|
||||
/**
|
||||
* Generates a segmentation from a given segmentation ID.
|
||||
* This function retrieves the associated segmentation and
|
||||
* its referenced volume, extracts label maps from the
|
||||
* segmentation volume, and produces segmentation data
|
||||
* alongside associated metadata.
|
||||
*
|
||||
* @param {Object} params - Parameters for the function.
|
||||
* @param params.segmentationId - ID of the segmentation to be generated.
|
||||
* @param params.options - Optional configuration for the generation process.
|
||||
*
|
||||
* @returns Returns the generated segmentation data.
|
||||
*/
|
||||
generateSegmentation: ({ segmentationId, options = {} }) => {
|
||||
const segmentation = cornerstoneToolsSegmentation.state.getSegmentation(segmentationId);
|
||||
|
||||
const { referencedVolumeId } = segmentation.representationData.LABELMAP;
|
||||
|
||||
const segmentationVolume = cache.getVolume(segmentationId);
|
||||
const referencedVolume = cache.getVolume(referencedVolumeId);
|
||||
const referencedImages = referencedVolume.getCornerstoneImages();
|
||||
|
||||
const labelmapObj = generateLabelMaps2DFrom3D(segmentationVolume);
|
||||
|
||||
// Generate fake metadata as an example
|
||||
labelmapObj.metadata = [];
|
||||
|
||||
const segmentationInOHIF = segmentationService.getSegmentation(segmentationId);
|
||||
labelmapObj.segmentsOnLabelmap.forEach(segmentIndex => {
|
||||
// segmentation service already has a color for each segment
|
||||
const segment = segmentationInOHIF?.segments[segmentIndex];
|
||||
const { label, color } = segment;
|
||||
|
||||
const RecommendedDisplayCIELabValue = dcmjs.data.Colors.rgb2DICOMLAB(
|
||||
color.slice(0, 3).map(value => value / 255)
|
||||
).map(value => Math.round(value));
|
||||
|
||||
const segmentMetadata = {
|
||||
SegmentNumber: segmentIndex.toString(),
|
||||
SegmentLabel: label,
|
||||
SegmentAlgorithmType: 'MANUAL',
|
||||
SegmentAlgorithmName: 'OHIF Brush',
|
||||
RecommendedDisplayCIELabValue,
|
||||
SegmentedPropertyCategoryCodeSequence: {
|
||||
CodeValue: 'T-D0050',
|
||||
CodingSchemeDesignator: 'SRT',
|
||||
CodeMeaning: 'Tissue',
|
||||
},
|
||||
SegmentedPropertyTypeCodeSequence: {
|
||||
CodeValue: 'T-D0050',
|
||||
CodingSchemeDesignator: 'SRT',
|
||||
CodeMeaning: 'Tissue',
|
||||
},
|
||||
};
|
||||
labelmapObj.metadata[segmentIndex] = segmentMetadata;
|
||||
});
|
||||
|
||||
const generatedSegmentation = generateSegmentation(
|
||||
referencedImages,
|
||||
labelmapObj,
|
||||
metaData,
|
||||
options
|
||||
);
|
||||
|
||||
return generatedSegmentation;
|
||||
},
|
||||
/**
|
||||
* Downloads a segmentation based on the provided segmentation ID.
|
||||
* This function retrieves the associated segmentation and
|
||||
* uses it to generate the corresponding DICOM dataset, which
|
||||
* is then downloaded with an appropriate filename.
|
||||
*
|
||||
* @param {Object} params - Parameters for the function.
|
||||
* @param params.segmentationId - ID of the segmentation to be downloaded.
|
||||
*
|
||||
*/
|
||||
downloadSegmentation: ({ segmentationId }) => {
|
||||
const segmentationInOHIF = segmentationService.getSegmentation(segmentationId);
|
||||
const generatedSegmentation = actions.generateSegmentation({
|
||||
segmentationId,
|
||||
});
|
||||
|
||||
downloadDICOMData(generatedSegmentation.dataset, `${segmentationInOHIF.label}`);
|
||||
},
|
||||
/**
|
||||
* Stores a segmentation based on the provided segmentationId into a specified data source.
|
||||
* The SeriesDescription is derived from user input or defaults to the segmentation label,
|
||||
* and in its absence, defaults to 'Research Derived Series'.
|
||||
*
|
||||
* @param {Object} params - Parameters for the function.
|
||||
* @param params.segmentationId - ID of the segmentation to be stored.
|
||||
* @param params.dataSource - Data source where the generated segmentation will be stored.
|
||||
*
|
||||
* @returns {Object|void} Returns the naturalized report if successfully stored,
|
||||
* otherwise throws an error.
|
||||
*/
|
||||
storeSegmentation: async ({ segmentationId, dataSource }) => {
|
||||
const promptResult = await createReportDialogPrompt(uiDialogService, {
|
||||
extensionManager,
|
||||
});
|
||||
|
||||
if (promptResult.action !== 1 && promptResult.value) {
|
||||
return;
|
||||
}
|
||||
|
||||
const segmentation = segmentationService.getSegmentation(segmentationId);
|
||||
|
||||
if (!segmentation) {
|
||||
throw new Error('No segmentation found');
|
||||
}
|
||||
|
||||
const { label } = segmentation;
|
||||
const SeriesDescription = promptResult.value || label || 'Research Derived Series';
|
||||
|
||||
const generatedData = actions.generateSegmentation({
|
||||
segmentationId,
|
||||
options: {
|
||||
SeriesDescription,
|
||||
},
|
||||
});
|
||||
|
||||
if (!generatedData || !generatedData.dataset) {
|
||||
throw new Error('Error during segmentation generation');
|
||||
}
|
||||
|
||||
const { dataset: naturalizedReport } = generatedData;
|
||||
|
||||
await dataSource.store.dicom(naturalizedReport);
|
||||
|
||||
// The "Mode" route listens for DicomMetadataStore changes
|
||||
// When a new instance is added, it listens and
|
||||
// automatically calls makeDisplaySets
|
||||
|
||||
// add the information for where we stored it to the instance as well
|
||||
naturalizedReport.wadoRoot = dataSource.getConfig().wadoRoot;
|
||||
|
||||
DicomMetadataStore.addInstances([naturalizedReport], true);
|
||||
|
||||
return naturalizedReport;
|
||||
},
|
||||
/**
|
||||
* Converts segmentations into RTSS for download.
|
||||
* This sample function retrieves all segentations and passes to
|
||||
* cornerstone tool adapter to convert to DICOM RTSS format. It then
|
||||
* converts dataset to downloadable blob.
|
||||
*
|
||||
*/
|
||||
downloadRTSS: ({ segmentationId }) => {
|
||||
const segmentations = segmentationService.getSegmentation(segmentationId);
|
||||
const vtkUtils = {
|
||||
vtkImageMarchingSquares,
|
||||
vtkDataArray,
|
||||
vtkImageData,
|
||||
};
|
||||
|
||||
const RTSS = generateRTSSFromSegmentations(
|
||||
segmentations,
|
||||
classes.MetadataProvider,
|
||||
DicomMetadataStore,
|
||||
cache,
|
||||
cornerstoneToolsEnums,
|
||||
vtkUtils
|
||||
);
|
||||
|
||||
try {
|
||||
const reportBlob = datasetToBlob(RTSS);
|
||||
|
||||
//Create a URL for the binary.
|
||||
const objectUrl = URL.createObjectURL(reportBlob);
|
||||
window.location.assign(objectUrl);
|
||||
} catch (e) {
|
||||
console.warn(e);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
const definitions = {
|
||||
getUpdatedViewportsForSegmentation: {
|
||||
commandFn: actions.getUpdatedViewportsForSegmentation,
|
||||
},
|
||||
loadSegmentationDisplaySetsForViewport: {
|
||||
commandFn: actions.loadSegmentationDisplaySetsForViewport,
|
||||
},
|
||||
loadSegmentationsForViewport: {
|
||||
commandFn: actions.loadSegmentationsForViewport,
|
||||
},
|
||||
createEmptySegmentationForViewport: {
|
||||
commandFn: actions.createEmptySegmentationForViewport,
|
||||
},
|
||||
generateSegmentation: {
|
||||
commandFn: actions.generateSegmentation,
|
||||
},
|
||||
downloadSegmentation: {
|
||||
commandFn: actions.downloadSegmentation,
|
||||
},
|
||||
storeSegmentation: {
|
||||
commandFn: actions.storeSegmentation,
|
||||
},
|
||||
downloadRTSS: {
|
||||
commandFn: actions.downloadRTSS,
|
||||
},
|
||||
};
|
||||
|
||||
return {
|
||||
actions,
|
||||
definitions,
|
||||
};
|
||||
};
|
||||
|
||||
export default commandsModule;
|
||||
@@ -0,0 +1,70 @@
|
||||
import React from 'react';
|
||||
|
||||
import { useAppConfig } from '@state';
|
||||
import PanelSegmentation from './panels/PanelSegmentation';
|
||||
import SegmentationToolbox from './panels/SegmentationToolbox';
|
||||
|
||||
const getPanelModule = ({ commandsManager, servicesManager, extensionManager, configuration }) => {
|
||||
const { customizationService } = servicesManager.services;
|
||||
|
||||
const wrappedPanelSegmentation = configuration => {
|
||||
const [appConfig] = useAppConfig();
|
||||
|
||||
const disableEditingForMode = customizationService.get('segmentation.disableEditing');
|
||||
|
||||
return (
|
||||
<PanelSegmentation
|
||||
commandsManager={commandsManager}
|
||||
servicesManager={servicesManager}
|
||||
extensionManager={extensionManager}
|
||||
configuration={{
|
||||
...configuration,
|
||||
disableEditing: appConfig.disableEditing || disableEditingForMode?.value,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const wrappedPanelSegmentationWithTools = configuration => {
|
||||
const [appConfig] = useAppConfig();
|
||||
return (
|
||||
<>
|
||||
<SegmentationToolbox
|
||||
commandsManager={commandsManager}
|
||||
servicesManager={servicesManager}
|
||||
extensionManager={extensionManager}
|
||||
configuration={{
|
||||
...configuration,
|
||||
}}
|
||||
/>
|
||||
<PanelSegmentation
|
||||
commandsManager={commandsManager}
|
||||
servicesManager={servicesManager}
|
||||
extensionManager={extensionManager}
|
||||
configuration={{
|
||||
...configuration,
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
return [
|
||||
{
|
||||
name: 'panelSegmentation',
|
||||
iconName: 'tab-segmentation',
|
||||
iconLabel: 'Segmentation',
|
||||
label: 'Segmentation',
|
||||
component: wrappedPanelSegmentation,
|
||||
},
|
||||
{
|
||||
name: 'panelSegmentationWithTools',
|
||||
iconName: 'tab-segmentation',
|
||||
iconLabel: 'Segmentation',
|
||||
label: 'Segmentation',
|
||||
component: wrappedPanelSegmentationWithTools,
|
||||
},
|
||||
];
|
||||
};
|
||||
|
||||
export default getPanelModule;
|
||||
@@ -57,10 +57,11 @@ function _getDisplaySetsFromSeries(instances, servicesManager, extensionManager)
|
||||
const referencedSeriesSequence = instance.ReferencedSeriesSequence;
|
||||
|
||||
if (!referencedSeriesSequence) {
|
||||
throw new Error('ReferencedSeriesSequence is missing for the SEG');
|
||||
console.error('ReferencedSeriesSequence is missing for the SEG');
|
||||
return;
|
||||
}
|
||||
|
||||
const referencedSeries = referencedSeriesSequence[0];
|
||||
const referencedSeries = referencedSeriesSequence[0] || referencedSeriesSequence;
|
||||
|
||||
displaySet.referencedImages = instance.ReferencedSeriesSequence.ReferencedInstanceSequence;
|
||||
displaySet.referencedSeriesInstanceUID = referencedSeries.SeriesInstanceUID;
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
import { id } from './id';
|
||||
import React from 'react';
|
||||
|
||||
import { Types } from '@ohif/core';
|
||||
|
||||
import getSopClassHandlerModule from './getSopClassHandlerModule';
|
||||
import PanelSegmentation from './panels/PanelSegmentation';
|
||||
import getHangingProtocolModule from './getHangingProtocolModule';
|
||||
import hydrateSEGDisplaySet from './utils/_hydrateSEG';
|
||||
import getPanelModule from './getPanelModule';
|
||||
import getCommandsModule from './commandsModule';
|
||||
import preRegistration from './init';
|
||||
|
||||
const Component = React.lazy(() => {
|
||||
return import(/* webpackPrefetch: true */ './viewports/OHIFCornerstoneSEGViewport');
|
||||
@@ -29,6 +28,7 @@ const extension = {
|
||||
* You ID can be anything you want, but it should be unique.
|
||||
*/
|
||||
id,
|
||||
preRegistration,
|
||||
|
||||
/**
|
||||
* PanelModule should provide a list of panels that will be available in OHIF
|
||||
@@ -36,27 +36,8 @@ const extension = {
|
||||
* iconName, iconLabel, label, component} object. Example of a panel module
|
||||
* is the StudyBrowserPanel that is provided by the default extension in OHIF.
|
||||
*/
|
||||
getPanelModule: ({ servicesManager, commandsManager, extensionManager }): Types.Panel[] => {
|
||||
const wrappedPanelSegmentation = () => {
|
||||
return (
|
||||
<PanelSegmentation
|
||||
commandsManager={commandsManager}
|
||||
servicesManager={servicesManager}
|
||||
extensionManager={extensionManager}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
return [
|
||||
{
|
||||
name: 'panelSegmentation',
|
||||
iconName: 'tab-segmentation',
|
||||
iconLabel: 'Segmentation',
|
||||
label: 'Segmentation',
|
||||
component: wrappedPanelSegmentation,
|
||||
},
|
||||
];
|
||||
},
|
||||
getPanelModule,
|
||||
getCommandsModule,
|
||||
|
||||
getViewportModule({ servicesManager, extensionManager }) {
|
||||
const ExtendedOHIFCornerstoneSEGViewport = props => {
|
||||
@@ -83,4 +64,3 @@ const extension = {
|
||||
};
|
||||
|
||||
export default extension;
|
||||
export { hydrateSEGDisplaySet };
|
||||
@@ -0,0 +1,5 @@
|
||||
import { addTool, BrushTool } from '@cornerstonejs/tools';
|
||||
|
||||
export default function init({ configuration = {} }): void {
|
||||
addTool(BrushTool);
|
||||
}
|
||||
@@ -1,16 +1,22 @@
|
||||
import { createReportAsync } from '@ohif/extension-default';
|
||||
import React, { useEffect, useState, useCallback } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { SegmentationGroupTable } from '@ohif/ui';
|
||||
import { SegmentationGroupTable, LegacyButtonGroup, LegacyButton } from '@ohif/ui';
|
||||
|
||||
import callInputDialog from './callInputDialog';
|
||||
import { useAppConfig } from '@state';
|
||||
import callColorPickerDialog from './colorPickerDialog';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export default function PanelSegmentation({ servicesManager, commandsManager }) {
|
||||
const { segmentationService, uiDialogService } = servicesManager.services;
|
||||
const [appConfig] = useAppConfig();
|
||||
const disableEditing = appConfig?.disableEditing;
|
||||
export default function PanelSegmentation({
|
||||
servicesManager,
|
||||
commandsManager,
|
||||
extensionManager,
|
||||
configuration,
|
||||
}) {
|
||||
const { segmentationService, viewportGridService, uiDialogService } = servicesManager.services;
|
||||
|
||||
const { t } = useTranslation('PanelSegmentation');
|
||||
|
||||
const [selectedSegmentationId, setSelectedSegmentationId] = useState(null);
|
||||
const [segmentationConfiguration, setSegmentationConfiguration] = useState(
|
||||
segmentationService.getConfiguration()
|
||||
@@ -18,29 +24,6 @@ export default function PanelSegmentation({ servicesManager, commandsManager })
|
||||
|
||||
const [segmentations, setSegmentations] = useState(() => segmentationService.getSegmentations());
|
||||
|
||||
const [isMinimized, setIsMinimized] = useState({});
|
||||
|
||||
const onToggleMinimizeSegmentation = useCallback(
|
||||
id => {
|
||||
setIsMinimized(prevState => ({
|
||||
...prevState,
|
||||
[id]: !prevState[id],
|
||||
}));
|
||||
},
|
||||
[setIsMinimized]
|
||||
);
|
||||
|
||||
// Only expand the last segmentation added to the list and collapse the rest
|
||||
useEffect(() => {
|
||||
const lastSegmentationId = segmentations[segmentations.length - 1]?.id;
|
||||
if (lastSegmentationId) {
|
||||
setIsMinimized(prevState => ({
|
||||
...prevState,
|
||||
[lastSegmentationId]: false,
|
||||
}));
|
||||
}
|
||||
}, [segmentations, setIsMinimized]);
|
||||
|
||||
useEffect(() => {
|
||||
// ~~ Subscription
|
||||
const added = segmentationService.EVENTS.SEGMENTATION_ADDED;
|
||||
@@ -64,6 +47,16 @@ export default function PanelSegmentation({ servicesManager, commandsManager })
|
||||
};
|
||||
}, []);
|
||||
|
||||
const getToolGroupIds = segmentationId => {
|
||||
const toolGroupIds = segmentationService.getToolGroupIdsWithSegmentation(segmentationId);
|
||||
|
||||
return toolGroupIds;
|
||||
};
|
||||
|
||||
const onSegmentationAdd = async () => {
|
||||
commandsManager.runCommand('createEmptySegmentationForViewport');
|
||||
};
|
||||
|
||||
const onSegmentationClick = (segmentationId: string) => {
|
||||
segmentationService.setActiveSegmentationForToolGroup(segmentationId);
|
||||
};
|
||||
@@ -72,14 +65,12 @@ export default function PanelSegmentation({ servicesManager, commandsManager })
|
||||
segmentationService.remove(segmentationId);
|
||||
};
|
||||
|
||||
const getToolGroupIds = segmentationId => {
|
||||
const toolGroupIds = segmentationService.getToolGroupIdsWithSegmentation(segmentationId);
|
||||
|
||||
return toolGroupIds;
|
||||
const onSegmentAdd = segmentationId => {
|
||||
segmentationService.addSegment(segmentationId);
|
||||
};
|
||||
|
||||
const onSegmentClick = (segmentationId, segmentIndex) => {
|
||||
segmentationService.setActiveSegmentForSegmentation(segmentationId, segmentIndex);
|
||||
segmentationService.setActiveSegment(segmentationId, segmentIndex);
|
||||
|
||||
const toolGroupIds = getToolGroupIds(segmentationId);
|
||||
|
||||
@@ -101,7 +92,7 @@ export default function PanelSegmentation({ servicesManager, commandsManager })
|
||||
return;
|
||||
}
|
||||
|
||||
segmentationService.setSegmentLabelForSegmentation(segmentationId, segmentIndex, label);
|
||||
segmentationService.setSegmentLabel(segmentationId, segmentIndex, label);
|
||||
});
|
||||
};
|
||||
|
||||
@@ -126,16 +117,34 @@ export default function PanelSegmentation({ servicesManager, commandsManager })
|
||||
};
|
||||
|
||||
const onSegmentColorClick = (segmentationId, segmentIndex) => {
|
||||
// Todo: Implement color picker later
|
||||
return;
|
||||
const segmentation = segmentationService.getSegmentation(segmentationId);
|
||||
|
||||
const segment = segmentation.segments[segmentIndex];
|
||||
const { color, opacity } = segment;
|
||||
|
||||
const rgbaColor = {
|
||||
r: color[0],
|
||||
g: color[1],
|
||||
b: color[2],
|
||||
a: opacity / 255.0,
|
||||
};
|
||||
|
||||
callColorPickerDialog(uiDialogService, rgbaColor, (newRgbaColor, actionId) => {
|
||||
if (actionId === 'cancel') {
|
||||
return;
|
||||
}
|
||||
|
||||
segmentationService.setSegmentRGBAColor(segmentationId, segmentIndex, [
|
||||
newRgbaColor.r,
|
||||
newRgbaColor.g,
|
||||
newRgbaColor.b,
|
||||
newRgbaColor.a * 255.0,
|
||||
]);
|
||||
});
|
||||
};
|
||||
|
||||
const onSegmentDelete = (segmentationId, segmentIndex) => {
|
||||
// segmentationService.removeSegmentFromSegmentation(
|
||||
// segmentationId,
|
||||
// segmentIndex
|
||||
// );
|
||||
console.warn('not implemented yet');
|
||||
segmentationService.removeSegment(segmentationId, segmentIndex);
|
||||
};
|
||||
|
||||
const onToggleSegmentVisibility = (segmentationId, segmentIndex) => {
|
||||
@@ -155,6 +164,10 @@ export default function PanelSegmentation({ servicesManager, commandsManager })
|
||||
});
|
||||
};
|
||||
|
||||
const onToggleSegmentLock = (segmentationId, segmentIndex) => {
|
||||
segmentationService.toggleSegmentLocked(segmentationId, segmentIndex);
|
||||
};
|
||||
|
||||
const onToggleSegmentationVisibility = segmentationId => {
|
||||
segmentationService.toggleSegmentationVisibility(segmentationId);
|
||||
};
|
||||
@@ -169,27 +182,69 @@ export default function PanelSegmentation({ servicesManager, commandsManager })
|
||||
[segmentationService]
|
||||
);
|
||||
|
||||
const onSegmentationDownload = segmentationId => {
|
||||
commandsManager.runCommand('downloadSegmentation', {
|
||||
segmentationId,
|
||||
});
|
||||
};
|
||||
|
||||
const storeSegmentation = async segmentationId => {
|
||||
const datasources = extensionManager.getActiveDataSource();
|
||||
|
||||
const displaySetInstanceUIDs = await createReportAsync({
|
||||
servicesManager,
|
||||
getReport: () =>
|
||||
commandsManager.runCommand('storeSegmentation', {
|
||||
segmentationId,
|
||||
dataSource: datasources[0],
|
||||
}),
|
||||
reportType: 'Segmentation',
|
||||
});
|
||||
|
||||
// Show the exported report in the active viewport as read only (similar to SR)
|
||||
if (displaySetInstanceUIDs) {
|
||||
// clear the segmentation that we exported, similar to the storeMeasurement
|
||||
// where we remove the measurements and prompt again the user if they would like
|
||||
// to re-read the measurements in a SR read only viewport
|
||||
segmentationService.remove(segmentationId);
|
||||
|
||||
viewportGridService.setDisplaySetsForViewport({
|
||||
viewportId: viewportGridService.getActiveViewportId(),
|
||||
displaySetInstanceUIDs,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const onSegmentationDownloadRTSS = segmentationId => {
|
||||
commandsManager.runCommand('downloadRTSS', {
|
||||
segmentationId,
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="mt-1 flex min-h-0 flex-auto flex-col justify-between">
|
||||
{/* show segmentation table */}
|
||||
{segmentations?.length ? (
|
||||
<>
|
||||
<div className="ohif-scrollbar flex min-h-0 flex-auto select-none flex-col justify-between overflow-auto">
|
||||
<SegmentationGroupTable
|
||||
title={t('Segmentations')}
|
||||
showAddSegmentation={false}
|
||||
segmentations={segmentations}
|
||||
isMinimized={isMinimized}
|
||||
disableEditing={configuration.disableEditing}
|
||||
activeSegmentationId={selectedSegmentationId || ''}
|
||||
onSegmentationAdd={onSegmentationAdd}
|
||||
onSegmentationClick={onSegmentationClick}
|
||||
onSegmentationDelete={onSegmentationDelete}
|
||||
onSegmentationDownload={onSegmentationDownload}
|
||||
onSegmentationDownloadRTSS={onSegmentationDownloadRTSS}
|
||||
storeSegmentation={storeSegmentation}
|
||||
onSegmentationEdit={onSegmentationEdit}
|
||||
onSegmentClick={onSegmentClick}
|
||||
onSegmentEdit={onSegmentEdit}
|
||||
disableEditing={disableEditing}
|
||||
onSegmentAdd={onSegmentAdd}
|
||||
onSegmentColorClick={onSegmentColorClick}
|
||||
onSegmentDelete={onSegmentDelete}
|
||||
onToggleSegmentVisibility={onToggleSegmentVisibility}
|
||||
onToggleSegmentLock={onToggleSegmentLock}
|
||||
onToggleSegmentationVisibility={onToggleSegmentationVisibility}
|
||||
onToggleMinimizeSegmentation={onToggleMinimizeSegmentation}
|
||||
showDeleteSegment={true}
|
||||
segmentationConfig={{ initialConfig: segmentationConfiguration }}
|
||||
setRenderOutline={value =>
|
||||
_setSegmentationConfiguration(selectedSegmentationId, 'renderOutline', value)
|
||||
@@ -217,8 +272,8 @@ export default function PanelSegmentation({ servicesManager, commandsManager })
|
||||
_setSegmentationConfiguration(selectedSegmentationId, 'fillAlphaInactive', value)
|
||||
}
|
||||
/>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,405 @@
|
||||
import React, { useCallback, useEffect, useState, useReducer } from 'react';
|
||||
import { AdvancedToolbox, InputDoubleRange, useViewportGrid } from '@ohif/ui';
|
||||
import { Types } from '@ohif/extension-cornerstone';
|
||||
import { utilities } from '@cornerstonejs/tools';
|
||||
|
||||
const { segmentation: segmentationUtils } = utilities;
|
||||
|
||||
const TOOL_TYPES = {
|
||||
CIRCULAR_BRUSH: 'CircularBrush',
|
||||
SPHERE_BRUSH: 'SphereBrush',
|
||||
CIRCULAR_ERASER: 'CircularEraser',
|
||||
SPHERE_ERASER: 'SphereEraser',
|
||||
CIRCLE_SHAPE: 'CircleScissor',
|
||||
RECTANGLE_SHAPE: 'RectangleScissor',
|
||||
SPHERE_SHAPE: 'SphereScissor',
|
||||
THRESHOLD_CIRCULAR_BRUSH: 'ThresholdCircularBrush',
|
||||
THRESHOLD_SPHERE_BRUSH: 'ThresholdSphereBrush',
|
||||
};
|
||||
|
||||
const ACTIONS = {
|
||||
SET_TOOL_CONFIG: 'SET_TOOL_CONFIG',
|
||||
SET_ACTIVE_TOOL: 'SET_ACTIVE_TOOL',
|
||||
};
|
||||
|
||||
const initialState = {
|
||||
Brush: {
|
||||
brushSize: 15,
|
||||
mode: 'CircularBrush', // Can be 'CircularBrush' or 'SphereBrush'
|
||||
},
|
||||
Eraser: {
|
||||
brushSize: 15,
|
||||
mode: 'CircularEraser', // Can be 'CircularEraser' or 'SphereEraser'
|
||||
},
|
||||
Shapes: {
|
||||
brushSize: 15,
|
||||
mode: 'CircleScissor', // E.g., 'CircleScissor', 'RectangleScissor', or 'SphereScissor'
|
||||
},
|
||||
ThresholdBrush: {
|
||||
brushSize: 15,
|
||||
thresholdRange: [-500, 500],
|
||||
},
|
||||
activeTool: null,
|
||||
};
|
||||
|
||||
function toolboxReducer(state, action) {
|
||||
switch (action.type) {
|
||||
case ACTIONS.SET_TOOL_CONFIG:
|
||||
const { tool, config } = action.payload;
|
||||
return {
|
||||
...state,
|
||||
[tool]: {
|
||||
...state[tool],
|
||||
...config,
|
||||
},
|
||||
};
|
||||
case ACTIONS.SET_ACTIVE_TOOL:
|
||||
return { ...state, activeTool: action.payload };
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
}
|
||||
|
||||
function SegmentationToolbox({ servicesManager, extensionManager }) {
|
||||
const { toolbarService, segmentationService, toolGroupService } =
|
||||
servicesManager.services as Types.CornerstoneServices;
|
||||
|
||||
const [viewportGrid] = useViewportGrid();
|
||||
const { viewports, activeViewportId } = viewportGrid;
|
||||
|
||||
const [toolsEnabled, setToolsEnabled] = useState(false);
|
||||
const [state, dispatch] = useReducer(toolboxReducer, initialState);
|
||||
|
||||
const updateActiveTool = useCallback(() => {
|
||||
if (!viewports?.size || activeViewportId === undefined) {
|
||||
return;
|
||||
}
|
||||
const viewport = viewports.get(activeViewportId);
|
||||
|
||||
if (!viewport) {
|
||||
return;
|
||||
}
|
||||
|
||||
dispatch({
|
||||
type: ACTIONS.SET_ACTIVE_TOOL,
|
||||
payload: toolGroupService.getActiveToolForViewport(viewport.viewportId),
|
||||
});
|
||||
}, [activeViewportId, viewports, toolGroupService, dispatch]);
|
||||
|
||||
const setToolActive = useCallback(
|
||||
toolName => {
|
||||
toolbarService.recordInteraction({
|
||||
interactionType: 'tool',
|
||||
commands: [
|
||||
{
|
||||
commandName: 'setToolActive',
|
||||
commandOptions: {
|
||||
toolName,
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
dispatch({ type: ACTIONS.SET_ACTIVE_TOOL, payload: toolName });
|
||||
},
|
||||
[toolbarService, dispatch]
|
||||
);
|
||||
|
||||
/**
|
||||
* sets the tools enabled IF there are segmentations
|
||||
*/
|
||||
useEffect(() => {
|
||||
const events = [
|
||||
segmentationService.EVENTS.SEGMENTATION_ADDED,
|
||||
segmentationService.EVENTS.SEGMENTATION_UPDATED,
|
||||
segmentationService.EVENTS.SEGMENTATION_REMOVED,
|
||||
];
|
||||
|
||||
const unsubscriptions = [];
|
||||
|
||||
events.forEach(event => {
|
||||
const { unsubscribe } = segmentationService.subscribe(event, () => {
|
||||
const segmentations = segmentationService.getSegmentations();
|
||||
|
||||
const activeSegmentation = segmentations?.find(seg => seg.isActive);
|
||||
|
||||
setToolsEnabled(activeSegmentation?.segmentCount > 0);
|
||||
});
|
||||
|
||||
unsubscriptions.push(unsubscribe);
|
||||
});
|
||||
|
||||
updateActiveTool();
|
||||
|
||||
return () => {
|
||||
unsubscriptions.forEach(unsubscribe => unsubscribe());
|
||||
};
|
||||
}, [activeViewportId, viewports, segmentationService, updateActiveTool]);
|
||||
|
||||
/**
|
||||
* Update the active tool when the toolbar state changes
|
||||
*/
|
||||
useEffect(() => {
|
||||
const { unsubscribe } = toolbarService.subscribe(
|
||||
toolbarService.EVENTS.TOOL_BAR_STATE_MODIFIED,
|
||||
() => {
|
||||
updateActiveTool();
|
||||
}
|
||||
);
|
||||
|
||||
return () => {
|
||||
unsubscribe();
|
||||
};
|
||||
}, [toolbarService, updateActiveTool]);
|
||||
|
||||
useEffect(() => {
|
||||
// if the active tool is not a brush tool then do nothing
|
||||
if (!Object.values(TOOL_TYPES).includes(state.activeTool)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// if the tool is Segmentation and it is enabled then do nothing
|
||||
if (toolsEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
// if the tool is Segmentation and it is disabled, then switch
|
||||
// back to the window level tool to not confuse the user when no
|
||||
// segmentation is active or when there is no segment in the segmentation
|
||||
setToolActive('WindowLevel');
|
||||
}, [toolsEnabled, state.activeTool, setToolActive]);
|
||||
|
||||
const updateBrushSize = useCallback(
|
||||
(toolName, brushSize) => {
|
||||
toolGroupService.getToolGroupIds()?.forEach(toolGroupId => {
|
||||
segmentationUtils.setBrushSizeForToolGroup(toolGroupId, brushSize, toolName);
|
||||
});
|
||||
},
|
||||
[toolGroupService]
|
||||
);
|
||||
|
||||
const onBrushSizeChange = useCallback(
|
||||
(valueAsStringOrNumber, toolCategory) => {
|
||||
const value = Number(valueAsStringOrNumber);
|
||||
|
||||
_getToolNamesFromCategory(toolCategory).forEach(toolName => {
|
||||
updateBrushSize(toolName, value);
|
||||
});
|
||||
|
||||
dispatch({
|
||||
type: ACTIONS.SET_TOOL_CONFIG,
|
||||
payload: {
|
||||
tool: toolCategory,
|
||||
config: { brushSize: value },
|
||||
},
|
||||
});
|
||||
},
|
||||
[toolGroupService, dispatch]
|
||||
);
|
||||
|
||||
const handleRangeChange = useCallback(
|
||||
newRange => {
|
||||
if (
|
||||
newRange[0] === state.ThresholdBrush.thresholdRange[0] &&
|
||||
newRange[1] === state.ThresholdBrush.thresholdRange[1]
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
const toolNames = _getToolNamesFromCategory('ThresholdBrush');
|
||||
|
||||
toolNames.forEach(toolName => {
|
||||
toolGroupService.getToolGroupIds()?.forEach(toolGroupId => {
|
||||
const toolGroup = toolGroupService.getToolGroup(toolGroupId);
|
||||
toolGroup.setToolConfiguration(toolName, {
|
||||
strategySpecificConfiguration: {
|
||||
THRESHOLD_INSIDE_CIRCLE: {
|
||||
threshold: newRange,
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
dispatch({
|
||||
type: ACTIONS.SET_TOOL_CONFIG,
|
||||
payload: {
|
||||
tool: 'ThresholdBrush',
|
||||
config: { thresholdRange: newRange },
|
||||
},
|
||||
});
|
||||
},
|
||||
[toolGroupService, dispatch, state.ThresholdBrush.thresholdRange]
|
||||
);
|
||||
|
||||
return (
|
||||
<AdvancedToolbox
|
||||
title="Segmentation Tools"
|
||||
items={[
|
||||
{
|
||||
name: 'Brush',
|
||||
icon: 'icon-tool-brush',
|
||||
disabled: !toolsEnabled,
|
||||
active:
|
||||
state.activeTool === TOOL_TYPES.CIRCULAR_BRUSH ||
|
||||
state.activeTool === TOOL_TYPES.SPHERE_BRUSH,
|
||||
onClick: () => setToolActive(TOOL_TYPES.CIRCULAR_BRUSH),
|
||||
options: [
|
||||
{
|
||||
name: 'Radius (mm)',
|
||||
id: 'brush-radius',
|
||||
type: 'range',
|
||||
min: 0.5,
|
||||
max: 99.5,
|
||||
value: state.Brush.brushSize,
|
||||
step: 0.5,
|
||||
onChange: value => onBrushSizeChange(value, 'Brush'),
|
||||
},
|
||||
{
|
||||
name: 'Mode',
|
||||
type: 'radio',
|
||||
id: 'brush-mode',
|
||||
value: state.Brush.mode,
|
||||
values: [
|
||||
{ value: TOOL_TYPES.CIRCULAR_BRUSH, label: 'Circle' },
|
||||
{ value: TOOL_TYPES.SPHERE_BRUSH, label: 'Sphere' },
|
||||
],
|
||||
onChange: value => setToolActive(value),
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'Eraser',
|
||||
icon: 'icon-tool-eraser',
|
||||
disabled: !toolsEnabled,
|
||||
active:
|
||||
state.activeTool === TOOL_TYPES.CIRCULAR_ERASER ||
|
||||
state.activeTool === TOOL_TYPES.SPHERE_ERASER,
|
||||
onClick: () => setToolActive(TOOL_TYPES.CIRCULAR_ERASER),
|
||||
options: [
|
||||
{
|
||||
name: 'Radius (mm)',
|
||||
type: 'range',
|
||||
id: 'eraser-radius',
|
||||
min: 0.5,
|
||||
max: 99.5,
|
||||
value: state.Eraser.brushSize,
|
||||
step: 0.5,
|
||||
onChange: value => onBrushSizeChange(value, 'Eraser'),
|
||||
},
|
||||
{
|
||||
name: 'Mode',
|
||||
type: 'radio',
|
||||
id: 'eraser-mode',
|
||||
value: state.Eraser.mode,
|
||||
values: [
|
||||
{ value: TOOL_TYPES.CIRCULAR_ERASER, label: 'Circle' },
|
||||
{ value: TOOL_TYPES.SPHERE_ERASER, label: 'Sphere' },
|
||||
],
|
||||
onChange: value => setToolActive(value),
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'Shapes',
|
||||
icon: 'icon-tool-shape',
|
||||
disabled: !toolsEnabled,
|
||||
active:
|
||||
state.activeTool === TOOL_TYPES.CIRCLE_SHAPE ||
|
||||
state.activeTool === TOOL_TYPES.RECTANGLE_SHAPE ||
|
||||
state.activeTool === TOOL_TYPES.SPHERE_SHAPE,
|
||||
onClick: () => setToolActive(TOOL_TYPES.CIRCLE_SHAPE),
|
||||
options: [
|
||||
{
|
||||
name: 'Mode',
|
||||
type: 'radio',
|
||||
value: state.Shapes.mode,
|
||||
id: 'shape-mode',
|
||||
values: [
|
||||
{ value: TOOL_TYPES.CIRCLE_SHAPE, label: 'Circle' },
|
||||
{ value: TOOL_TYPES.RECTANGLE_SHAPE, label: 'Rectangle' },
|
||||
{ value: TOOL_TYPES.SPHERE_SHAPE, label: 'Sphere' },
|
||||
],
|
||||
onChange: value => setToolActive(value),
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'Threshold Tool',
|
||||
icon: 'icon-tool-threshold',
|
||||
disabled: !toolsEnabled,
|
||||
active:
|
||||
state.activeTool === TOOL_TYPES.THRESHOLD_CIRCULAR_BRUSH ||
|
||||
state.activeTool === TOOL_TYPES.THRESHOLD_SPHERE_BRUSH,
|
||||
onClick: () => setToolActive(TOOL_TYPES.THRESHOLD_CIRCULAR_BRUSH),
|
||||
options: [
|
||||
{
|
||||
name: 'Radius (mm)',
|
||||
id: 'threshold-radius',
|
||||
type: 'range',
|
||||
min: 0.5,
|
||||
max: 99.5,
|
||||
value: state.ThresholdBrush.brushSize,
|
||||
step: 0.5,
|
||||
onChange: value => onBrushSizeChange(value, 'ThresholdBrush'),
|
||||
},
|
||||
{
|
||||
name: 'Mode',
|
||||
type: 'radio',
|
||||
id: 'threshold-mode',
|
||||
value: state.activeTool,
|
||||
values: [
|
||||
{ value: TOOL_TYPES.THRESHOLD_CIRCULAR_BRUSH, label: 'Circle' },
|
||||
{ value: TOOL_TYPES.THRESHOLD_SPHERE_BRUSH, label: 'Sphere' },
|
||||
],
|
||||
onChange: value => setToolActive(value),
|
||||
},
|
||||
{
|
||||
type: 'custom',
|
||||
id: 'segmentation-threshold-range',
|
||||
children: () => {
|
||||
return (
|
||||
<div>
|
||||
<div className="bg-secondary-light h-[1px]"></div>
|
||||
<div className="mt-1 text-[13px] text-white">Threshold</div>
|
||||
<InputDoubleRange
|
||||
values={state.ThresholdBrush.thresholdRange}
|
||||
onChange={handleRangeChange}
|
||||
minValue={-1000}
|
||||
maxValue={1000}
|
||||
step={1}
|
||||
showLabel={true}
|
||||
allowNumberEdit={true}
|
||||
showAdjustmentArrows={false}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
]}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function _getToolNamesFromCategory(category) {
|
||||
let toolNames = [];
|
||||
switch (category) {
|
||||
case 'Brush':
|
||||
toolNames = ['CircularBrush', 'SphereBrush'];
|
||||
break;
|
||||
case 'Eraser':
|
||||
toolNames = ['CircularEraser', 'SphereEraser'];
|
||||
break;
|
||||
case 'ThresholdBrush':
|
||||
toolNames = ['ThresholdCircularBrush', 'ThresholdSphereBrush'];
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return toolNames;
|
||||
}
|
||||
|
||||
export default SegmentationToolbox;
|
||||
@@ -0,0 +1,3 @@
|
||||
.chrome-picker {
|
||||
background: #090c29 !important;
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
import React from 'react';
|
||||
import { Dialog } from '@ohif/ui';
|
||||
import { ChromePicker } from 'react-color';
|
||||
|
||||
import './colorPickerDialog.css';
|
||||
|
||||
function callColorPickerDialog(uiDialogService, rgbaColor, callback) {
|
||||
const dialogId = 'pick-color';
|
||||
|
||||
const onSubmitHandler = ({ action, value }) => {
|
||||
switch (action.id) {
|
||||
case 'save':
|
||||
callback(value.rgbaColor, action.id);
|
||||
break;
|
||||
case 'cancel':
|
||||
callback('', action.id);
|
||||
break;
|
||||
}
|
||||
uiDialogService.dismiss({ id: dialogId });
|
||||
};
|
||||
|
||||
if (uiDialogService) {
|
||||
uiDialogService.create({
|
||||
id: dialogId,
|
||||
centralize: true,
|
||||
isDraggable: false,
|
||||
showOverlay: true,
|
||||
content: Dialog,
|
||||
contentProps: {
|
||||
title: 'Segment Color',
|
||||
value: { rgbaColor },
|
||||
noCloseButton: true,
|
||||
onClose: () => uiDialogService.dismiss({ id: dialogId }),
|
||||
actions: [
|
||||
{ id: 'cancel', text: 'Cancel', type: 'primary' },
|
||||
{ id: 'save', text: 'Save', type: 'secondary' },
|
||||
],
|
||||
onSubmit: onSubmitHandler,
|
||||
body: ({ value, setValue }) => {
|
||||
const handleChange = color => {
|
||||
setValue({ rgbaColor: color.rgb });
|
||||
};
|
||||
|
||||
return (
|
||||
<ChromePicker
|
||||
color={value.rgbaColor}
|
||||
onChange={handleChange}
|
||||
presetColors={[]}
|
||||
width={300}
|
||||
/>
|
||||
);
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export default callColorPickerDialog;
|
||||
@@ -1,73 +0,0 @@
|
||||
async function _hydrateSEGDisplaySet({
|
||||
segDisplaySet,
|
||||
viewportId: targetViewportId,
|
||||
servicesManager,
|
||||
}) {
|
||||
const { segmentationService, hangingProtocolService, viewportGridService } =
|
||||
servicesManager.services;
|
||||
|
||||
const displaySetInstanceUID = segDisplaySet.referencedDisplaySetInstanceUID;
|
||||
|
||||
let segmentationId = null;
|
||||
|
||||
// We need the hydration to notify panels about the new segmentation added
|
||||
const suppressEvents = false;
|
||||
|
||||
segmentationId = await segmentationService.createSegmentationForSEGDisplaySet(
|
||||
segDisplaySet,
|
||||
segmentationId,
|
||||
suppressEvents
|
||||
);
|
||||
|
||||
segmentationService.hydrateSegmentation(segDisplaySet.displaySetInstanceUID);
|
||||
|
||||
const { viewports } = viewportGridService.getState();
|
||||
|
||||
const updatedViewports = hangingProtocolService.getViewportsRequireUpdate(
|
||||
targetViewportId,
|
||||
displaySetInstanceUID
|
||||
);
|
||||
|
||||
// Todo: fix this after we have a better way for stack viewport segmentations
|
||||
|
||||
// check every viewport in the viewports to see if the displaySetInstanceUID
|
||||
// is being displayed, if so we need to update the viewport to use volume viewport
|
||||
// (if already is not using it) since Cornerstone3D currently only supports
|
||||
// volume viewport for segmentation
|
||||
viewports.forEach((viewport, viewportId) => {
|
||||
if (targetViewportId === viewportId) {
|
||||
return;
|
||||
}
|
||||
|
||||
const shouldDisplaySeg = segmentationService.shouldRenderSegmentation(
|
||||
viewport.displaySetInstanceUIDs,
|
||||
segDisplaySet.displaySetInstanceUID
|
||||
);
|
||||
|
||||
if (shouldDisplaySeg) {
|
||||
updatedViewports.push({
|
||||
viewportId,
|
||||
displaySetInstanceUIDs: viewport.displaySetInstanceUIDs,
|
||||
viewportOptions: {
|
||||
// Note: This is a hack to get the grid to re-render the OHIFCornerstoneViewport component
|
||||
// Used for segmentation hydration right now, since the logic to decide whether
|
||||
// a viewport needs to render a segmentation lives inside the CornerstoneViewportService
|
||||
// so we need to re-render (force update via change of the needsRerendering) so that React
|
||||
// does the diffing and decides we should render this again (although the id and element has not changed)
|
||||
// so that the CornerstoneViewportService can decide whether to render the segmentation or not.
|
||||
needsRerendering: true,
|
||||
initialImageOptions: {
|
||||
preset: 'middle',
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Do the entire update at once
|
||||
viewportGridService.setDisplaySetsForViewports(updatedViewports);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
export default _hydrateSEGDisplaySet;
|
||||
@@ -0,0 +1,190 @@
|
||||
import { Enums, cache } from '@cornerstonejs/core';
|
||||
|
||||
/**
|
||||
* Updates the viewports in preparation for rendering segmentations.
|
||||
* Evaluates each viewport to determine which need modifications,
|
||||
* then for those viewports, changes them to a volume type and ensures
|
||||
* they are ready for segmentation rendering.
|
||||
*
|
||||
* @param {Object} params - Parameters for the function.
|
||||
* @param params.viewportId - ID of the viewport to be updated.
|
||||
* @param params.loadFn - Function to load the segmentation data.
|
||||
* @param params.servicesManager - The services manager.
|
||||
* @param params.referencedDisplaySetInstanceUID - Optional UID for the referenced display set instance.
|
||||
*
|
||||
* @returns Returns true upon successful update of viewports for segmentation rendering.
|
||||
*/
|
||||
async function updateViewportsForSegmentationRendering({
|
||||
viewportId,
|
||||
loadFn,
|
||||
servicesManager,
|
||||
referencedDisplaySetInstanceUID,
|
||||
}: {
|
||||
viewportId: string;
|
||||
loadFn: () => Promise<string>;
|
||||
servicesManager: any;
|
||||
referencedDisplaySetInstanceUID?: string;
|
||||
}) {
|
||||
const { cornerstoneViewportService, segmentationService, viewportGridService } =
|
||||
servicesManager.services;
|
||||
|
||||
const viewport = getTargetViewport({ viewportId, viewportGridService });
|
||||
const targetViewportId = viewport.viewportOptions.viewportId;
|
||||
|
||||
referencedDisplaySetInstanceUID =
|
||||
referencedDisplaySetInstanceUID || viewport?.displaySetInstanceUIDs[0];
|
||||
|
||||
const updatedViewports = getUpdatedViewportsForSegmentation({
|
||||
servicesManager,
|
||||
viewportId,
|
||||
referencedDisplaySetInstanceUID,
|
||||
});
|
||||
|
||||
// create Segmentation callback which needs to be waited until
|
||||
// the volume is created (if coming from stack)
|
||||
const createSegmentationForVolume = async () => {
|
||||
const segmentationId = await loadFn();
|
||||
segmentationService.hydrateSegmentation(segmentationId);
|
||||
};
|
||||
|
||||
// the reference volume that is used to draw the segmentation. so check if the
|
||||
// volume exists in the cache (the target Viewport is already a volume viewport)
|
||||
const volumeExists = Array.from(cache._volumeCache.keys()).some(volumeId =>
|
||||
volumeId.includes(referencedDisplaySetInstanceUID)
|
||||
);
|
||||
|
||||
updatedViewports.forEach(async viewport => {
|
||||
viewport.viewportOptions = {
|
||||
...viewport.viewportOptions,
|
||||
viewportType: 'volume',
|
||||
needsRerendering: true,
|
||||
};
|
||||
const viewportId = viewport.viewportId;
|
||||
|
||||
const csViewport = cornerstoneViewportService.getCornerstoneViewport(viewportId);
|
||||
const prevCamera = csViewport.getCamera();
|
||||
|
||||
// only run the createSegmentationForVolume for the targetViewportId
|
||||
// since the rest will get handled by cornerstoneViewportService
|
||||
if (volumeExists && viewportId === targetViewportId) {
|
||||
await createSegmentationForVolume();
|
||||
return;
|
||||
}
|
||||
|
||||
const createNewSegmentationWhenVolumeMounts = async evt => {
|
||||
const isTheActiveViewportVolumeMounted = evt.detail.volumeActors?.find(ac =>
|
||||
ac.uid.includes(referencedDisplaySetInstanceUID)
|
||||
);
|
||||
|
||||
// Note: make sure to re-grab the viewport since it might have changed
|
||||
// during the time it took for the volume to be mounted, for instance
|
||||
// the stack viewport has been changed to a volume viewport
|
||||
const volumeViewport = cornerstoneViewportService.getCornerstoneViewport(viewportId);
|
||||
volumeViewport.setCamera(prevCamera);
|
||||
|
||||
volumeViewport.element.removeEventListener(
|
||||
Enums.Events.VOLUME_VIEWPORT_NEW_VOLUME,
|
||||
createNewSegmentationWhenVolumeMounts
|
||||
);
|
||||
|
||||
if (!isTheActiveViewportVolumeMounted) {
|
||||
// it means it is one of those other updated viewports so just update the camera
|
||||
return;
|
||||
}
|
||||
|
||||
if (viewportId === targetViewportId) {
|
||||
await createSegmentationForVolume();
|
||||
}
|
||||
};
|
||||
|
||||
csViewport.element.addEventListener(
|
||||
Enums.Events.VOLUME_VIEWPORT_NEW_VOLUME,
|
||||
createNewSegmentationWhenVolumeMounts
|
||||
);
|
||||
});
|
||||
|
||||
// Set the displaySets for the viewports that require to be updated
|
||||
viewportGridService.setDisplaySetsForViewports(updatedViewports);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
const getTargetViewport = ({ viewportId, viewportGridService }) => {
|
||||
const { viewports, activeViewportId } = viewportGridService.getState();
|
||||
const targetViewportId = viewportId || activeViewportId;
|
||||
|
||||
const viewport = viewports.get(targetViewportId);
|
||||
|
||||
return viewport;
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieves a list of viewports that require updates in preparation for segmentation rendering.
|
||||
* This function evaluates viewports based on their compatibility with the provided segmentation's
|
||||
* frame of reference UID and appends them to the updated list if they should render the segmentation.
|
||||
*
|
||||
* @param {Object} params - Parameters for the function.
|
||||
* @param params.viewportId - the ID of the viewport to be updated.
|
||||
* @param params.servicesManager - The services manager
|
||||
* @param params.referencedDisplaySetInstanceUID - Optional UID for the referenced display set instance.
|
||||
*
|
||||
* @returns {Array} Returns an array of viewports that require updates for segmentation rendering.
|
||||
*/
|
||||
function getUpdatedViewportsForSegmentation({
|
||||
viewportId,
|
||||
servicesManager,
|
||||
referencedDisplaySetInstanceUID,
|
||||
}) {
|
||||
const { hangingProtocolService, displaySetService, segmentationService, viewportGridService } =
|
||||
servicesManager.services;
|
||||
|
||||
const { viewports } = viewportGridService.getState();
|
||||
|
||||
const viewport = getTargetViewport({ viewportId, viewportGridService });
|
||||
const targetViewportId = viewport.viewportOptions.viewportId;
|
||||
|
||||
const displaySetInstanceUIDs = viewports.get(targetViewportId).displaySetInstanceUIDs;
|
||||
|
||||
const referenceDisplaySetInstanceUID =
|
||||
referencedDisplaySetInstanceUID || displaySetInstanceUIDs[0];
|
||||
|
||||
const referencedDisplaySet = displaySetService.getDisplaySetByUID(referenceDisplaySetInstanceUID);
|
||||
const segmentationFrameOfReferenceUID = referencedDisplaySet.instances[0].FrameOfReferenceUID;
|
||||
|
||||
const updatedViewports = hangingProtocolService.getViewportsRequireUpdate(
|
||||
targetViewportId,
|
||||
referenceDisplaySetInstanceUID
|
||||
);
|
||||
|
||||
viewports.forEach((viewport, viewportId) => {
|
||||
if (
|
||||
targetViewportId === viewportId ||
|
||||
updatedViewports.find(v => v.viewportId === viewportId)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
const shouldDisplaySeg = segmentationService.shouldRenderSegmentation(
|
||||
viewport.displaySetInstanceUIDs,
|
||||
segmentationFrameOfReferenceUID
|
||||
);
|
||||
|
||||
if (shouldDisplaySeg) {
|
||||
updatedViewports.push({
|
||||
viewportId,
|
||||
displaySetInstanceUIDs: viewport.displaySetInstanceUIDs,
|
||||
viewportOptions: {
|
||||
viewportType: 'volume',
|
||||
needsRerendering: true,
|
||||
},
|
||||
});
|
||||
}
|
||||
});
|
||||
return updatedViewports;
|
||||
}
|
||||
|
||||
export {
|
||||
updateViewportsForSegmentationRendering,
|
||||
getUpdatedViewportsForSegmentation,
|
||||
getTargetViewport,
|
||||
};
|
||||
@@ -1,7 +1,7 @@
|
||||
function createSEGToolGroupAndAddTools(ToolGroupService, customizationService, toolGroupId) {
|
||||
const { tools } = customizationService.get('cornerstone.overlayViewportTools') ?? {};
|
||||
|
||||
return ToolGroupService.createToolGroupAndAddTools(toolGroupId, tools, {});
|
||||
return ToolGroupService.createToolGroupAndAddTools(toolGroupId, tools);
|
||||
}
|
||||
|
||||
export default createSEGToolGroupAndAddTools;
|
||||
@@ -1,5 +1,4 @@
|
||||
import { ButtonEnums } from '@ohif/ui';
|
||||
import hydrateSEGDisplaySet from './_hydrateSEG';
|
||||
|
||||
const RESPONSE = {
|
||||
NO_NEVER: -1,
|
||||
@@ -7,7 +6,13 @@ const RESPONSE = {
|
||||
HYDRATE_SEG: 5,
|
||||
};
|
||||
|
||||
function promptHydrateSEG({ servicesManager, segDisplaySet, viewportId, preHydrateCallbacks }) {
|
||||
function promptHydrateSEG({
|
||||
servicesManager,
|
||||
segDisplaySet,
|
||||
viewportId,
|
||||
preHydrateCallbacks,
|
||||
hydrateSEGDisplaySet,
|
||||
}) {
|
||||
const { uiViewportDialogService } = servicesManager.services;
|
||||
|
||||
return new Promise(async function (resolve, reject) {
|
||||
@@ -21,7 +26,6 @@ function promptHydrateSEG({ servicesManager, segDisplaySet, viewportId, preHydra
|
||||
const isHydrated = await hydrateSEGDisplaySet({
|
||||
segDisplaySet,
|
||||
viewportId,
|
||||
servicesManager,
|
||||
});
|
||||
|
||||
resolve(isHydrated);
|
||||
|
||||
@@ -5,7 +5,6 @@ import OHIF, { utils } from '@ohif/core';
|
||||
import { LoadingIndicatorTotalPercent, useViewportGrid, ViewportActionBar } from '@ohif/ui';
|
||||
import createSEGToolGroupAndAddTools from '../utils/initSEGToolGroup';
|
||||
import promptHydrateSEG from '../utils/promptHydrateSEG';
|
||||
import hydrateSEGDisplaySet from '../utils/_hydrateSEG';
|
||||
import _getStatusComponent from './_getStatusComponent';
|
||||
|
||||
const { formatDate } = utils;
|
||||
@@ -158,6 +157,7 @@ function OHIFCornerstoneSEGViewport(props) {
|
||||
viewportId,
|
||||
segDisplaySet,
|
||||
preHydrateCallbacks: [storePresentationState],
|
||||
hydrateSEGDisplaySet,
|
||||
}).then(isHydrated => {
|
||||
if (isHydrated) {
|
||||
setIsHydrated(true);
|
||||
@@ -291,6 +291,13 @@ function OHIFCornerstoneSEGViewport(props) {
|
||||
SpacingBetweenSlices,
|
||||
} = referencedDisplaySetRef.current.metadata;
|
||||
|
||||
const hydrateSEGDisplaySet = ({ segDisplaySet, viewportId }) => {
|
||||
commandsManager.runCommand('loadSegmentationDisplaySetsForViewport', {
|
||||
displaySets: [segDisplaySet],
|
||||
viewportId,
|
||||
});
|
||||
};
|
||||
|
||||
const onStatusClick = async () => {
|
||||
// Before hydrating a SEG and make it added to all viewports in the grid
|
||||
// that share the same frameOfReferenceUID, we need to store the viewport grid
|
||||
@@ -302,7 +309,6 @@ function OHIFCornerstoneSEGViewport(props) {
|
||||
const isHydrated = await hydrateSEGDisplaySet({
|
||||
segDisplaySet,
|
||||
viewportId,
|
||||
servicesManager,
|
||||
});
|
||||
|
||||
setIsHydrated(isHydrated);
|
||||
@@ -369,12 +375,18 @@ OHIFCornerstoneSEGViewport.defaultProps = {
|
||||
};
|
||||
|
||||
function _getReferencedDisplaySetMetadata(referencedDisplaySet, segDisplaySet) {
|
||||
const {
|
||||
SharedFunctionalGroupsSequence: [SharedFunctionalGroup],
|
||||
} = segDisplaySet.instance;
|
||||
const {
|
||||
PixelMeasuresSequence: [PixelMeasures],
|
||||
} = SharedFunctionalGroup;
|
||||
const { SharedFunctionalGroupsSequence } = segDisplaySet.instance;
|
||||
|
||||
const SharedFunctionalGroup = Array.isArray(SharedFunctionalGroupsSequence)
|
||||
? SharedFunctionalGroupsSequence[0]
|
||||
: SharedFunctionalGroupsSequence;
|
||||
|
||||
const { PixelMeasuresSequence } = SharedFunctionalGroup;
|
||||
|
||||
const PixelMeasures = Array.isArray(PixelMeasuresSequence)
|
||||
? PixelMeasuresSequence[0]
|
||||
: PixelMeasuresSequence;
|
||||
|
||||
const { SpacingBetweenSlices, SliceThickness } = PixelMeasures;
|
||||
|
||||
const image0 = referencedDisplaySet.images[0];
|
||||
|
||||
@@ -3,6 +3,387 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [3.7.0](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.110...v3.7.0) (2023-10-11)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-sr
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.110](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.109...v3.7.0-beta.110) (2023-10-11)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-sr
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.109](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.108...v3.7.0-beta.109) (2023-10-11)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-sr
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.108](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.107...v3.7.0-beta.108) (2023-10-10)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-sr
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.107](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.106...v3.7.0-beta.107) (2023-10-10)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-sr
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.106](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.105...v3.7.0-beta.106) (2023-10-10)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-sr
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.105](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.104...v3.7.0-beta.105) (2023-10-10)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **voi:** should publish voi change event on reset ([#3707](https://github.com/OHIF/Viewers/issues/3707)) ([52f34c6](https://github.com/OHIF/Viewers/commit/52f34c64d014f433ec1661a39b47e7fb27f15332))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.104](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.103...v3.7.0-beta.104) (2023-10-09)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **modality unit:** fix the modality unit per target via upgrade of cs3d ([#3706](https://github.com/OHIF/Viewers/issues/3706)) ([0a42d57](https://github.com/OHIF/Viewers/commit/0a42d573bbca7f2551a831a46d3aa6b56674a580))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.103](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.102...v3.7.0-beta.103) (2023-10-09)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-sr
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.102](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.101...v3.7.0-beta.102) (2023-10-06)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **Segmentation:** download RTSS from Labelmap([#3692](https://github.com/OHIF/Viewers/issues/3692)) ([40673f6](https://github.com/OHIF/Viewers/commit/40673f64b36b1150149c55632aa1825178a39e65))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.101](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.100...v3.7.0-beta.101) (2023-10-06)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-sr
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.100](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.99...v3.7.0-beta.100) (2023-10-06)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-sr
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.99](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.98...v3.7.0-beta.99) (2023-10-04)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **measurement and microscopy:** various small fixes for measurement and microscopy side panel ([#3696](https://github.com/OHIF/Viewers/issues/3696)) ([c1d5ee7](https://github.com/OHIF/Viewers/commit/c1d5ee7e3f7f4c0c6bed9ae81eba5519741c5155))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.98](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.97...v3.7.0-beta.98) (2023-10-04)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-sr
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.97](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.96...v3.7.0-beta.97) (2023-10-04)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-sr
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.96](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.95...v3.7.0-beta.96) (2023-10-04)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-sr
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.95](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.94...v3.7.0-beta.95) (2023-10-04)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-sr
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.94](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.93...v3.7.0-beta.94) (2023-10-03)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-sr
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.93](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.92...v3.7.0-beta.93) (2023-10-03)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-sr
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.92](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.91...v3.7.0-beta.92) (2023-10-03)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-sr
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.91](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.90...v3.7.0-beta.91) (2023-10-03)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-sr
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.90](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.89...v3.7.0-beta.90) (2023-10-03)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-sr
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.89](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.88...v3.7.0-beta.89) (2023-10-03)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-sr
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.88](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.87...v3.7.0-beta.88) (2023-10-03)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **config:** support more values for the useSharedArrayBuffer ([#3688](https://github.com/OHIF/Viewers/issues/3688)) ([1129c15](https://github.com/OHIF/Viewers/commit/1129c155d2c7d46c98a5df7c09879aa3d459fa7e))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.87](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.86...v3.7.0-beta.87) (2023-09-29)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-sr
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.86](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.85...v3.7.0-beta.86) (2023-09-29)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-sr
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.85](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.84...v3.7.0-beta.85) (2023-09-26)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-sr
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.84](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.83...v3.7.0-beta.84) (2023-09-26)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-sr
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.83](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.82...v3.7.0-beta.83) (2023-09-26)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-sr
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.82](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.81...v3.7.0-beta.82) (2023-09-26)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-sr
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.81](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.80...v3.7.0-beta.81) (2023-09-26)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-sr
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.80](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.79...v3.7.0-beta.80) (2023-09-22)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **segmentation mode:** Add create, and export SEG with Brushes ([#3632](https://github.com/OHIF/Viewers/issues/3632)) ([48bbd62](https://github.com/OHIF/Viewers/commit/48bbd6281a497ea68670239f5426a10ee6c56dc1))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.79](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.78...v3.7.0-beta.79) (2023-09-22)
|
||||
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* **memory:** add 16 bit texture via configuration - reduces memory by half ([#3662](https://github.com/OHIF/Viewers/issues/3662)) ([2bd3b26](https://github.com/OHIF/Viewers/commit/2bd3b26a6aa54b211ef988f3ad64ef1fe5648bab))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.78](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.77...v3.7.0-beta.78) (2023-09-21)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-sr
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.77](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.76...v3.7.0-beta.77) (2023-09-21)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-sr
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.76](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.75...v3.7.0-beta.76) (2023-09-19)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-sr
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.75](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.74...v3.7.0-beta.75) (2023-09-18)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-sr
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.74](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.73...v3.7.0-beta.74) (2023-09-15)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-sr
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.73](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.72...v3.7.0-beta.73) (2023-09-12)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-sr
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.72](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.71...v3.7.0-beta.72) (2023-09-12)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-sr
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.71](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.70...v3.7.0-beta.71) (2023-09-12)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-sr
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.70](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.69...v3.7.0-beta.70) (2023-09-12)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-sr
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.69](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.68...v3.7.0-beta.69) (2023-09-11)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-sr
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.68](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.67...v3.7.0-beta.68) (2023-09-11)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-sr
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.67](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.66...v3.7.0-beta.67) (2023-09-06)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-sr
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.66](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.65...v3.7.0-beta.66) (2023-09-06)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-sr
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@ohif/extension-cornerstone-dicom-sr",
|
||||
"version": "3.7.0-beta.66",
|
||||
"version": "3.7.0",
|
||||
"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.66",
|
||||
"@ohif/extension-cornerstone": "3.7.0-beta.66",
|
||||
"@ohif/extension-measurement-tracking": "3.7.0-beta.66",
|
||||
"@ohif/ui": "3.7.0-beta.66",
|
||||
"@ohif/core": "3.7.0",
|
||||
"@ohif/extension-cornerstone": "3.7.0",
|
||||
"@ohif/extension-measurement-tracking": "3.7.0",
|
||||
"@ohif/ui": "3.7.0",
|
||||
"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.13.2",
|
||||
"@cornerstonejs/core": "^1.13.2",
|
||||
"@cornerstonejs/tools": "^1.13.2",
|
||||
"@cornerstonejs/adapters": "^1.20.3",
|
||||
"@cornerstonejs/core": "^1.20.3",
|
||||
"@cornerstonejs/tools": "^1.20.3",
|
||||
"classnames": "^2.3.2"
|
||||
}
|
||||
}
|
||||
@@ -54,7 +54,7 @@ const dicomSRExtension = {
|
||||
},
|
||||
getCommandsModule,
|
||||
getSopClassHandlerModule,
|
||||
// Include dynmically computed values such as toolNames not known till instantiation
|
||||
// Include dynamically computed values such as toolNames not known till instantiation
|
||||
getUtilityModule({ servicesManager }) {
|
||||
return [
|
||||
{
|
||||
|
||||
@@ -44,7 +44,7 @@ const isStructuredReportSeries = series => {
|
||||
};
|
||||
|
||||
/**
|
||||
* Checkes if series1 is newer than series2
|
||||
* Checks if series1 is newer than series2
|
||||
*
|
||||
* @param {Object} series1 - Series Metadata 1
|
||||
* @param {Object} series2 - Series Metadata 2
|
||||
|
||||
@@ -240,7 +240,7 @@ function OHIFCornerstoneSRViewport(props) {
|
||||
const onDisplaySetsRemovedSubscription = displaySetService.subscribe(
|
||||
displaySetService.EVENTS.DISPLAY_SETS_REMOVED,
|
||||
({ displaySetInstanceUIDs }) => {
|
||||
const activeViewport = viewports[activeViewportId];
|
||||
const activeViewport = viewports.get(activeViewportId);
|
||||
if (displaySetInstanceUIDs.includes(activeViewport.displaySetInstanceUID)) {
|
||||
viewportGridService.setDisplaySetsForViewport({
|
||||
viewportId: activeViewportId,
|
||||
|
||||
@@ -3,6 +3,415 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [3.7.0](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.110...v3.7.0) (2023-10-11)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.110](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.109...v3.7.0-beta.110) (2023-10-11)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.109](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.108...v3.7.0-beta.109) (2023-10-11)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.108](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.107...v3.7.0-beta.108) (2023-10-10)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.107](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.106...v3.7.0-beta.107) (2023-10-10)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **modules:** add stylus loader as an option to be uncommented ([#3710](https://github.com/OHIF/Viewers/issues/3710)) ([7c57f67](https://github.com/OHIF/Viewers/commit/7c57f67844b790fc6e47ac3f9708bf9d576389c8))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.106](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.105...v3.7.0-beta.106) (2023-10-10)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **segmentation:** Various fixes for segmentation mode and other ([#3709](https://github.com/OHIF/Viewers/issues/3709)) ([a9a6ad5](https://github.com/OHIF/Viewers/commit/a9a6ad50eae67b43b8b34efc07182d788cacdcfe))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.105](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.104...v3.7.0-beta.105) (2023-10-10)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **voi:** should publish voi change event on reset ([#3707](https://github.com/OHIF/Viewers/issues/3707)) ([52f34c6](https://github.com/OHIF/Viewers/commit/52f34c64d014f433ec1661a39b47e7fb27f15332))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.104](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.103...v3.7.0-beta.104) (2023-10-09)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **modality unit:** fix the modality unit per target via upgrade of cs3d ([#3706](https://github.com/OHIF/Viewers/issues/3706)) ([0a42d57](https://github.com/OHIF/Viewers/commit/0a42d573bbca7f2551a831a46d3aa6b56674a580))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.103](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.102...v3.7.0-beta.103) (2023-10-09)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **segmentation:** do not use SAB if not specified ([#3705](https://github.com/OHIF/Viewers/issues/3705)) ([4911e47](https://github.com/OHIF/Viewers/commit/4911e4796cef5e22cb7cc0ca73dc5c956bc75339))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.102](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.101...v3.7.0-beta.102) (2023-10-06)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **Segmentation:** download RTSS from Labelmap([#3692](https://github.com/OHIF/Viewers/issues/3692)) ([40673f6](https://github.com/OHIF/Viewers/commit/40673f64b36b1150149c55632aa1825178a39e65))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.101](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.100...v3.7.0-beta.101) (2023-10-06)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **bugs:** fixing lots of bugs regarding release candidate ([#3700](https://github.com/OHIF/Viewers/issues/3700)) ([8bc12a3](https://github.com/OHIF/Viewers/commit/8bc12a37d0353160ae5ea4624dc0b244b7d59c07))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.100](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.99...v3.7.0-beta.100) (2023-10-06)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.99](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.98...v3.7.0-beta.99) (2023-10-04)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **measurement and microscopy:** various small fixes for measurement and microscopy side panel ([#3696](https://github.com/OHIF/Viewers/issues/3696)) ([c1d5ee7](https://github.com/OHIF/Viewers/commit/c1d5ee7e3f7f4c0c6bed9ae81eba5519741c5155))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.98](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.97...v3.7.0-beta.98) (2023-10-04)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.97](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.96...v3.7.0-beta.97) (2023-10-04)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.96](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.95...v3.7.0-beta.96) (2023-10-04)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.95](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.94...v3.7.0-beta.95) (2023-10-04)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.94](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.93...v3.7.0-beta.94) (2023-10-03)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **debug:** Add timing information about time to first image/all images, and query time ([#3681](https://github.com/OHIF/Viewers/issues/3681)) ([108383b](https://github.com/OHIF/Viewers/commit/108383b9ef51e4bef82d9c932b9bc7aa5354e799))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.93](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.92...v3.7.0-beta.93) (2023-10-03)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **displayArea:** add display area to hanging protocol ([#3691](https://github.com/OHIF/Viewers/issues/3691)) ([5e7fe91](https://github.com/OHIF/Viewers/commit/5e7fe91617d7399f85702d82e7bfa028b8010a89))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.92](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.91...v3.7.0-beta.92) (2023-10-03)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.91](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.90...v3.7.0-beta.91) (2023-10-03)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.90](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.89...v3.7.0-beta.90) (2023-10-03)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.89](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.88...v3.7.0-beta.89) (2023-10-03)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **dicom overlay:** Handle special cases of ArrayBuffer for various DICOM overlay attributes. ([#3684](https://github.com/OHIF/Viewers/issues/3684)) ([e36a604](https://github.com/OHIF/Viewers/commit/e36a6043315e900eeb6ce183772c7f852f478e96))
|
||||
* **StackSync:** Miscellaneous fixes for stack image sync ([#3663](https://github.com/OHIF/Viewers/issues/3663)) ([8a335bd](https://github.com/OHIF/Viewers/commit/8a335bd03d14ba87d65d7468d93f74040aa828d9))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.88](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.87...v3.7.0-beta.88) (2023-10-03)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **config:** support more values for the useSharedArrayBuffer ([#3688](https://github.com/OHIF/Viewers/issues/3688)) ([1129c15](https://github.com/OHIF/Viewers/commit/1129c155d2c7d46c98a5df7c09879aa3d459fa7e))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.87](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.86...v3.7.0-beta.87) (2023-09-29)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **no sab:** should work when shared array buffer is not required ([#3686](https://github.com/OHIF/Viewers/issues/3686)) ([a67d72d](https://github.com/OHIF/Viewers/commit/a67d72de85238b369a18c010bf6d147daefc6df5))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.86](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.85...v3.7.0-beta.86) (2023-09-29)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.85](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.84...v3.7.0-beta.85) (2023-09-26)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.84](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.83...v3.7.0-beta.84) (2023-09-26)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.83](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.82...v3.7.0-beta.83) (2023-09-26)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.82](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.81...v3.7.0-beta.82) (2023-09-26)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.81](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.80...v3.7.0-beta.81) (2023-09-26)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.80](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.79...v3.7.0-beta.80) (2023-09-22)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **segmentation mode:** Add create, and export SEG with Brushes ([#3632](https://github.com/OHIF/Viewers/issues/3632)) ([48bbd62](https://github.com/OHIF/Viewers/commit/48bbd6281a497ea68670239f5426a10ee6c56dc1))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.79](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.78...v3.7.0-beta.79) (2023-09-22)
|
||||
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* **memory:** add 16 bit texture via configuration - reduces memory by half ([#3662](https://github.com/OHIF/Viewers/issues/3662)) ([2bd3b26](https://github.com/OHIF/Viewers/commit/2bd3b26a6aa54b211ef988f3ad64ef1fe5648bab))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.78](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.77...v3.7.0-beta.78) (2023-09-21)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.77](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.76...v3.7.0-beta.77) (2023-09-21)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.76](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.75...v3.7.0-beta.76) (2023-09-19)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.75](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.74...v3.7.0-beta.75) (2023-09-18)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.74](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.73...v3.7.0-beta.74) (2023-09-15)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **measurements:** Update the calibration tool to match changes in CS3D ([#3505](https://github.com/OHIF/Viewers/issues/3505)) ([38af311](https://github.com/OHIF/Viewers/commit/38af3112ec1f94f36c0ef64ff1cf9d21c0981c81))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.73](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.72...v3.7.0-beta.73) (2023-09-12)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.72](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.71...v3.7.0-beta.72) (2023-09-12)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.71](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.70...v3.7.0-beta.71) (2023-09-12)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.70](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.69...v3.7.0-beta.70) (2023-09-12)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.69](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.68...v3.7.0-beta.69) (2023-09-11)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.68](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.67...v3.7.0-beta.68) (2023-09-11)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.67](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.66...v3.7.0-beta.67) (2023-09-06)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.66](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.65...v3.7.0-beta.66) (2023-09-06)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@ohif/extension-cornerstone",
|
||||
"version": "3.7.0-beta.66",
|
||||
"version": "3.7.0",
|
||||
"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.13.2",
|
||||
"@ohif/core": "3.7.0-beta.66",
|
||||
"@ohif/ui": "3.7.0-beta.66",
|
||||
"@cornerstonejs/dicom-image-loader": "^1.20.3",
|
||||
"@ohif/core": "3.7.0",
|
||||
"@ohif/ui": "3.7.0",
|
||||
"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.13.2",
|
||||
"@cornerstonejs/core": "^1.13.2",
|
||||
"@cornerstonejs/streaming-image-volume-loader": "^1.13.2",
|
||||
"@cornerstonejs/tools": "^1.13.2",
|
||||
"@cornerstonejs/adapters": "^1.20.3",
|
||||
"@cornerstonejs/core": "^1.20.3",
|
||||
"@cornerstonejs/streaming-image-volume-loader": "^1.20.3",
|
||||
"@cornerstonejs/tools": "^1.20.3",
|
||||
"@kitware/vtk.js": "27.3.1",
|
||||
"html2canvas": "^1.4.1",
|
||||
"lodash.debounce": "4.0.8",
|
||||
|
||||
@@ -75,7 +75,7 @@ export function formatPN(name) {
|
||||
* Gets compression type
|
||||
*
|
||||
* @param {number} imageId
|
||||
* @returns {string} comrpession type.
|
||||
* @returns {string} compression type.
|
||||
*/
|
||||
export function getCompression(imageId) {
|
||||
const generalImageModule = metaData.get('generalImageModule', imageId) || {};
|
||||
|
||||
@@ -18,7 +18,6 @@ import toggleStackImageSync from './utils/stackSync/toggleStackImageSync';
|
||||
import { getFirstAnnotationSelected } from './utils/measurementServiceMappings/utils/selection';
|
||||
import getActiveViewportEnabledElement from './utils/getActiveViewportEnabledElement';
|
||||
import { CornerstoneServices } from './types';
|
||||
import ImageOverlayViewerTool from './tools/ImageOverlayViewerTool';
|
||||
|
||||
function commandsModule({
|
||||
servicesManager,
|
||||
@@ -29,7 +28,6 @@ function commandsModule({
|
||||
toolGroupService,
|
||||
cineService,
|
||||
toolbarService,
|
||||
stateSyncService,
|
||||
uiDialogService,
|
||||
cornerstoneViewportService,
|
||||
uiNotificationService,
|
||||
@@ -156,7 +154,7 @@ function commandsModule({
|
||||
* @param props - containing the updates to apply
|
||||
* @param props.measurementKey - chooses the measurement key to apply the
|
||||
* code to. This will typically be finding or site to apply a
|
||||
* finind code or a findingSites code.
|
||||
* finding code or a findingSites code.
|
||||
* @param props.code - A coding scheme value from DICOM, including:
|
||||
* * CodeValue - the language independent code, for example '1234'
|
||||
* * CodingSchemeDesignator - the issue of the code value
|
||||
@@ -226,6 +224,25 @@ function commandsModule({
|
||||
arrowTextCallback: ({ callback, data }) => {
|
||||
callInputDialog(uiDialogService, data, callback);
|
||||
},
|
||||
cleanUpCrosshairs: () => {
|
||||
// if the crosshairs tool is active, deactivate it and set window level active
|
||||
// since we are going back to main non-mpr HP
|
||||
const activeViewportToolGroup = toolGroupService.getToolGroup(null);
|
||||
|
||||
if (activeViewportToolGroup._toolInstances?.Crosshairs?.mode === Enums.ToolModes.Active) {
|
||||
actions.toolbarServiceRecordInteraction({
|
||||
interactionType: 'tool',
|
||||
commands: [
|
||||
{
|
||||
commandOptions: {
|
||||
toolName: 'WindowLevel',
|
||||
},
|
||||
context: 'CORNERSTONE',
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
},
|
||||
toggleCine: () => {
|
||||
const { viewports } = viewportGridService.getState();
|
||||
const { isCineEnabled } = cineService.getState();
|
||||
@@ -266,7 +283,11 @@ function commandsModule({
|
||||
toolbarServiceRecordInteraction: props => {
|
||||
toolbarService.recordInteraction(props);
|
||||
},
|
||||
|
||||
// Enable or disable a toggleable command, without calling the activation
|
||||
// Used to setup already active tools from hanging protocols
|
||||
setToolbarToggled: props => {
|
||||
toolbarService.setToggled(props.toolId, props.isActive ?? true);
|
||||
},
|
||||
setToolActive: ({ toolName, toolGroupId = null, toggledState }) => {
|
||||
if (toolName === 'Crosshairs') {
|
||||
const activeViewportToolGroup = toolGroupService.getToolGroup(null);
|
||||
@@ -291,18 +312,8 @@ function commandsModule({
|
||||
}
|
||||
|
||||
const toolGroup = toolGroupService.getToolGroup(toolGroupId);
|
||||
const toolGroupViewportIds = toolGroup?.getViewportIds?.();
|
||||
|
||||
// if toolGroup has been destroyed, or its viewports have been removed
|
||||
if (!toolGroupViewportIds || !toolGroupViewportIds.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
const filteredViewports = Array.from(viewports.values()).filter(viewport => {
|
||||
return toolGroupViewportIds.includes(viewport.viewportId);
|
||||
});
|
||||
|
||||
if (!filteredViewports.length) {
|
||||
if (!toolGroup) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -449,7 +460,7 @@ function commandsModule({
|
||||
viewport.resetProperties();
|
||||
viewport.resetCamera();
|
||||
} else {
|
||||
// Todo: add reset properties for volume viewport
|
||||
viewport.resetProperties();
|
||||
viewport.resetCamera();
|
||||
}
|
||||
|
||||
@@ -551,16 +562,16 @@ function commandsModule({
|
||||
|
||||
toggleStackImageSync: ({ toggledState }) => {
|
||||
toggleStackImageSync({
|
||||
getEnabledElement,
|
||||
servicesManager,
|
||||
toggledState,
|
||||
});
|
||||
},
|
||||
setSourceViewportForReferenceLinesTool: ({ toggledState }) => {
|
||||
const { activeViewportId } = viewportGridService.getState();
|
||||
const viewportInfo = cornerstoneViewportService.getViewportInfo(activeViewportId);
|
||||
setSourceViewportForReferenceLinesTool: ({ toggledState, viewportId }) => {
|
||||
if (!viewportId) {
|
||||
const { activeViewportId } = viewportGridService.getState();
|
||||
viewportId = activeViewportId;
|
||||
}
|
||||
|
||||
const viewportId = viewportInfo.getViewportId();
|
||||
const toolGroup = toolGroupService.getToolGroupForViewport(viewportId);
|
||||
|
||||
toolGroup.setToolConfiguration(
|
||||
@@ -701,8 +712,12 @@ function commandsModule({
|
||||
},
|
||||
storePresentation: {
|
||||
commandFn: actions.storePresentation,
|
||||
storeContexts: [],
|
||||
options: {},
|
||||
},
|
||||
setToolbarToggled: {
|
||||
commandFn: actions.setToolbarToggled,
|
||||
},
|
||||
cleanUpCrosshairs: {
|
||||
commandFn: actions.cleanUpCrosshairs,
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -13,12 +13,12 @@ function WrappedCinePlayer({ enabledVPElement, viewportId, servicesManager }) {
|
||||
const handleCineClose = () => {
|
||||
toolbarService.recordInteraction({
|
||||
groupId: 'MoreTools',
|
||||
itemId: 'cine',
|
||||
interactionType: 'toggle',
|
||||
commands: [
|
||||
{
|
||||
commandName: 'toggleCine',
|
||||
commandOptions: {},
|
||||
toolName: 'cine',
|
||||
context: 'CORNERSTONE',
|
||||
},
|
||||
],
|
||||
|
||||
@@ -24,18 +24,7 @@ const mpr: Types.HangingProtocol.Protocol = {
|
||||
// Turns off crosshairs when switching out of MPR mode
|
||||
onProtocolExit: [
|
||||
{
|
||||
commandName: 'toolbarServiceRecordInteraction',
|
||||
commandOptions: {
|
||||
interactionType: 'tool',
|
||||
commands: [
|
||||
{
|
||||
commandOptions: {
|
||||
toolName: 'WindowLevel',
|
||||
},
|
||||
context: 'CORNERSTONE',
|
||||
},
|
||||
],
|
||||
},
|
||||
commandName: 'cleanUpCrosshairs',
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
@@ -137,7 +137,7 @@ const cornerstoneExtension: Types.Extensions.Extension = {
|
||||
export type { PublicViewportOptions };
|
||||
export {
|
||||
measurementMappingUtils,
|
||||
CornerstoneExtensionTypes,
|
||||
CornerstoneExtensionTypes as Types,
|
||||
toolNames,
|
||||
getActiveViewportEnabledElement,
|
||||
};
|
||||
|
||||
@@ -12,8 +12,9 @@ import {
|
||||
imageLoadPoolManager,
|
||||
Settings,
|
||||
utilities as csUtilities,
|
||||
Enums as csEnums,
|
||||
} from '@cornerstonejs/core';
|
||||
import { Enums, utilities, ReferenceLinesTool } from '@cornerstonejs/tools';
|
||||
import { Enums } from '@cornerstonejs/tools';
|
||||
import { cornerstoneStreamingImageVolumeLoader } from '@cornerstonejs/streaming-image-volume-loader';
|
||||
|
||||
import initWADOImageLoader from './initWADOImageLoader';
|
||||
@@ -27,6 +28,7 @@ import interleaveTopToBottom from './utils/interleaveTopToBottom';
|
||||
import initContextMenu from './initContextMenu';
|
||||
import initDoubleClick from './initDoubleClick';
|
||||
import { CornerstoneServices } from './types';
|
||||
import initViewTiming from './utils/initViewTiming';
|
||||
|
||||
// TODO: Cypress tests are currently grabbing this from the window?
|
||||
window.cornerstone = cornerstone;
|
||||
@@ -41,10 +43,30 @@ export default async function init({
|
||||
configuration,
|
||||
appConfig,
|
||||
}: Types.Extensions.ExtensionParams): Promise<void> {
|
||||
await cs3DInit();
|
||||
// Note: this should run first before initializing the cornerstone
|
||||
// DO NOT CHANGE THE ORDER
|
||||
const value = appConfig.useSharedArrayBuffer;
|
||||
let sharedArrayBufferDisabled = false;
|
||||
|
||||
if (value === 'AUTO') {
|
||||
cornerstone.setUseSharedArrayBuffer(csEnums.SharedArrayBufferModes.AUTO);
|
||||
} else if (value === 'FALSE' || value === false) {
|
||||
cornerstone.setUseSharedArrayBuffer(csEnums.SharedArrayBufferModes.FALSE);
|
||||
sharedArrayBufferDisabled = true;
|
||||
} else {
|
||||
cornerstone.setUseSharedArrayBuffer(csEnums.SharedArrayBufferModes.TRUE);
|
||||
}
|
||||
|
||||
await cs3DInit({
|
||||
rendering: {
|
||||
preferSizeOverAccuracy: Boolean(appConfig.use16BitDataType),
|
||||
useNorm16Texture: Boolean(appConfig.use16BitDataType),
|
||||
},
|
||||
});
|
||||
|
||||
// For debugging e2e tests that are failing on CI
|
||||
cornerstone.setUseCPURendering(Boolean(appConfig.useCPURendering));
|
||||
|
||||
cornerstone.setConfiguration({
|
||||
...cornerstone.getConfiguration(),
|
||||
rendering: {
|
||||
@@ -72,6 +94,7 @@ export default async function init({
|
||||
cornerstoneViewportService,
|
||||
hangingProtocolService,
|
||||
toolGroupService,
|
||||
toolbarService,
|
||||
viewportGridService,
|
||||
stateSyncService,
|
||||
} = servicesManager.services as CornerstoneServices;
|
||||
@@ -80,10 +103,15 @@ export default async function init({
|
||||
window.extensionManager = extensionManager;
|
||||
window.commandsManager = commandsManager;
|
||||
|
||||
if (appConfig.showWarningMessageForCrossOrigin && !window.crossOriginIsolated) {
|
||||
if (
|
||||
appConfig.showWarningMessageForCrossOrigin &&
|
||||
!window.crossOriginIsolated &&
|
||||
!sharedArrayBufferDisabled
|
||||
) {
|
||||
uiNotificationService.show({
|
||||
title: 'Cross Origin Isolation',
|
||||
message: 'Cross Origin Isolation is not enabled, volume rendering will not work (e.g., MPR)',
|
||||
message:
|
||||
'Cross Origin Isolation is not enabled, read more about it here: https://docs.ohif.org/faq/',
|
||||
type: 'warning',
|
||||
});
|
||||
}
|
||||
@@ -182,9 +210,45 @@ export default async function init({
|
||||
commandsManager,
|
||||
});
|
||||
|
||||
const newStackCallback = evt => {
|
||||
/**
|
||||
* When a viewport gets a new display set, this call will go through all the
|
||||
* active tools in the toolbar, and call any commands registered in the
|
||||
* toolbar service with a callback to re-enable on displaying the viewport.
|
||||
*/
|
||||
const toolbarEventListener = evt => {
|
||||
const { element } = evt.detail;
|
||||
utilities.stackPrefetch.enable(element);
|
||||
const activeTools = toolbarService.getActiveTools();
|
||||
|
||||
activeTools.forEach(tool => {
|
||||
const toolData = toolbarService.getNestedButton(tool);
|
||||
const commands = toolData?.listeners?.[evt.type];
|
||||
commandsManager.run(commands, { element, evt });
|
||||
});
|
||||
};
|
||||
|
||||
/** Listens for active viewport events and fires the toolbar listeners */
|
||||
const activeViewportEventListener = evt => {
|
||||
const { viewportId } = evt;
|
||||
const toolGroup = toolGroupService.getToolGroupForViewport(viewportId);
|
||||
|
||||
const activeTools = toolbarService.getActiveTools();
|
||||
|
||||
activeTools.forEach(tool => {
|
||||
if (!toolGroup?._toolInstances?.[tool]) {
|
||||
return;
|
||||
}
|
||||
|
||||
// check if tool is active on the new viewport
|
||||
const toolEnabled = toolGroup._toolInstances[tool].mode === Enums.ToolModes.Enabled;
|
||||
|
||||
if (!toolEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
const button = toolbarService.getNestedButton(tool);
|
||||
const commands = button?.listeners?.[evt.type];
|
||||
commandsManager.run(commands, { viewportId, evt });
|
||||
});
|
||||
};
|
||||
|
||||
const resetCrosshairs = evt => {
|
||||
@@ -211,11 +275,18 @@ export default async function init({
|
||||
}
|
||||
};
|
||||
|
||||
eventTarget.addEventListener(EVENTS.STACK_VIEWPORT_NEW_STACK, evt => {
|
||||
const { element } = evt.detail;
|
||||
cornerstoneTools.utilities.stackContextPrefetch.enable(element);
|
||||
});
|
||||
|
||||
function elementEnabledHandler(evt) {
|
||||
const { element } = evt.detail;
|
||||
element.addEventListener(EVENTS.CAMERA_RESET, resetCrosshairs);
|
||||
|
||||
eventTarget.addEventListener(EVENTS.STACK_VIEWPORT_NEW_STACK, newStackCallback);
|
||||
eventTarget.addEventListener(EVENTS.STACK_VIEWPORT_NEW_STACK, toolbarEventListener);
|
||||
|
||||
initViewTiming({ element, eventTarget });
|
||||
}
|
||||
|
||||
function elementDisabledHandler(evt) {
|
||||
@@ -236,35 +307,9 @@ export default async function init({
|
||||
|
||||
viewportGridService.subscribe(
|
||||
viewportGridService.EVENTS.ACTIVE_VIEWPORT_ID_CHANGED,
|
||||
({ viewportId }) => {
|
||||
const toolGroup = toolGroupService.getToolGroupForViewport(viewportId);
|
||||
|
||||
if (!toolGroup || !toolGroup._toolInstances?.['ReferenceLines']) {
|
||||
return;
|
||||
}
|
||||
|
||||
// check if reference lines are active
|
||||
const referenceLinesEnabled =
|
||||
toolGroup._toolInstances['ReferenceLines'].mode === Enums.ToolModes.Enabled;
|
||||
|
||||
if (!referenceLinesEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
toolGroup.setToolConfiguration(
|
||||
ReferenceLinesTool.toolName,
|
||||
{
|
||||
sourceViewportId: viewportId,
|
||||
},
|
||||
true // overwrite
|
||||
activeViewportEventListener
|
||||
);
|
||||
|
||||
// make sure to set it to enabled again since we want to recalculate
|
||||
// the source-target lines
|
||||
toolGroup.setToolEnabled(ReferenceLinesTool.toolName);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
function CPUModal() {
|
||||
return (
|
||||
|
||||
@@ -18,6 +18,7 @@ const DEFAULT_CONTEXT_MENU_CLICKS = {
|
||||
{
|
||||
commandName: 'showCornerstoneContextMenu',
|
||||
commandOptions: {
|
||||
requireNearbyToolData: true,
|
||||
menuId: 'measurementsContextMenu',
|
||||
},
|
||||
},
|
||||
@@ -62,8 +63,20 @@ function initContextMenu({
|
||||
const customizations =
|
||||
customizationService.get('cornerstoneViewportClickCommands') || DEFAULT_CONTEXT_MENU_CLICKS;
|
||||
const toRun = customizations[name];
|
||||
|
||||
if (!toRun) {
|
||||
return;
|
||||
}
|
||||
|
||||
// only find nearbyToolData if required, for the click (which closes the context menu
|
||||
// we don't need to find nearbyToolData)
|
||||
let nearbyToolData = null;
|
||||
if (toRun.commands.some(command => command.commandOptions?.requireNearbyToolData)) {
|
||||
nearbyToolData = findNearbyToolData(commandsManager, evt);
|
||||
}
|
||||
|
||||
const options = {
|
||||
nearbyToolData: findNearbyToolData(commandsManager, evt),
|
||||
nearbyToolData,
|
||||
event: evt,
|
||||
};
|
||||
commandsManager.run(toRun, options);
|
||||
|
||||
@@ -25,6 +25,9 @@ import {
|
||||
annotation,
|
||||
ReferenceLinesTool,
|
||||
TrackballRotateTool,
|
||||
CircleScissorsTool,
|
||||
RectangleScissorsTool,
|
||||
SphereScissorsTool,
|
||||
} from '@cornerstonejs/tools';
|
||||
|
||||
import CalibrationLineTool from './tools/CalibrationLineTool';
|
||||
@@ -59,6 +62,9 @@ export default function initCornerstoneTools(configuration = {}) {
|
||||
addTool(ReferenceLinesTool);
|
||||
addTool(CalibrationLineTool);
|
||||
addTool(TrackballRotateTool);
|
||||
addTool(CircleScissorsTool);
|
||||
addTool(RectangleScissorsTool);
|
||||
addTool(SphereScissorsTool);
|
||||
addTool(ImageOverlayViewerTool);
|
||||
|
||||
// Modify annotation tools to use dashed lines on SR
|
||||
@@ -101,6 +107,9 @@ const toolNames = {
|
||||
ReferenceLines: ReferenceLinesTool.toolName,
|
||||
CalibrationLine: CalibrationLineTool.toolName,
|
||||
TrackballRotateTool: TrackballRotateTool.toolName,
|
||||
CircleScissors: CircleScissorsTool.toolName,
|
||||
RectangleScissors: RectangleScissorsTool.toolName,
|
||||
SphereScissors: SphereScissorsTool.toolName,
|
||||
ImageOverlayViewer: ImageOverlayViewerTool.toolName,
|
||||
};
|
||||
|
||||
|
||||
@@ -49,6 +49,7 @@ export default function initWADOImageLoader(
|
||||
// Until the default is set to true (which is the case for cornerstone3D),
|
||||
// we should set this flag to false.
|
||||
convertFloatPixelDataToInt: false,
|
||||
use16BitDataType: Boolean(appConfig.use16BitDataType),
|
||||
},
|
||||
beforeSend: function (xhr) {
|
||||
//TODO should be removed in the future and request emitted by DicomWebDataSource
|
||||
|
||||
+5
-2
@@ -210,7 +210,7 @@ class CornerstoneCacheService {
|
||||
}
|
||||
|
||||
private _shouldRenderSegmentation(displaySets) {
|
||||
const { segmentationService } = this.servicesManager.services;
|
||||
const { segmentationService, displaySetService } = this.servicesManager.services;
|
||||
|
||||
const viewportDisplaySetInstanceUIDs = displaySets.map(
|
||||
({ displaySetInstanceUID }) => displaySetInstanceUID
|
||||
@@ -222,10 +222,13 @@ class CornerstoneCacheService {
|
||||
|
||||
for (const segmentation of segmentations) {
|
||||
const segDisplaySetInstanceUID = segmentation.displaySetInstanceUID;
|
||||
const segDisplaySet = displaySetService.getDisplaySetByUID(segDisplaySetInstanceUID);
|
||||
|
||||
const instance = segDisplaySet.instances?.[0] || segDisplaySet.instance;
|
||||
|
||||
const shouldDisplaySeg = segmentationService.shouldRenderSegmentation(
|
||||
viewportDisplaySetInstanceUIDs,
|
||||
segDisplaySetInstanceUID
|
||||
instance.FrameOfReferenceUID
|
||||
);
|
||||
|
||||
if (shouldDisplaySeg) {
|
||||
|
||||
@@ -7,7 +7,6 @@ import {
|
||||
geometryLoader,
|
||||
eventTarget,
|
||||
getEnabledElementByIds,
|
||||
metaData,
|
||||
utilities as csUtils,
|
||||
volumeLoader,
|
||||
} from '@cornerstonejs/core';
|
||||
@@ -99,43 +98,51 @@ class SegmentationService extends PubSubService {
|
||||
};
|
||||
|
||||
/**
|
||||
* It adds a segment to a segmentation, basically just setting the properties for
|
||||
* the segment.
|
||||
* @param segmentationId - The ID of the segmentation you want to add a
|
||||
* segment to.
|
||||
* @param segmentIndex - The index of the segment to add.
|
||||
* @param properties - The properties of the segment to add including
|
||||
* -- label: the label of the segment
|
||||
* -- color: the color of the segment
|
||||
* -- opacity: the opacity of the segment
|
||||
* -- visibility: the visibility of the segment (boolean)
|
||||
* -- isLocked: whether the segment is locked for editing
|
||||
* -- active: whether the segment is currently the active segment to be edited
|
||||
* Adds a new segment to the specified segmentation.
|
||||
* @param segmentationId - The ID of the segmentation to add the segment to.
|
||||
* @param config - An object containing the configuration options for the new segment.
|
||||
* - segmentIndex: (optional) The index of the segment to add. If not provided, the next available index will be used.
|
||||
* - toolGroupId: (optional) The ID of the tool group to associate the new segment with. If not provided, the first available tool group will be used.
|
||||
* - properties: (optional) An object containing the properties of the new segment.
|
||||
* - label: (optional) The label of the new segment. If not provided, a default label will be used.
|
||||
* - color: (optional) The color of the new segment in RGB format. If not provided, a default color will be used.
|
||||
* - opacity: (optional) The opacity of the new segment. If not provided, a default opacity will be used.
|
||||
* - visibility: (optional) Whether the new segment should be visible. If not provided, the segment will be visible by default.
|
||||
* - isLocked: (optional) Whether the new segment should be locked for editing. If not provided, the segment will not be locked by default.
|
||||
* - active: (optional) Whether the new segment should be the active segment to be edited. If not provided, the segment will not be active by default.
|
||||
*/
|
||||
public addSegment(
|
||||
segmentationId: string,
|
||||
segmentIndex: number,
|
||||
toolGroupId?: string,
|
||||
properties?: {
|
||||
label?: string;
|
||||
color?: ohifTypes.RGB;
|
||||
opacity?: number;
|
||||
visibility?: boolean;
|
||||
isLocked?: boolean;
|
||||
active?: boolean;
|
||||
}
|
||||
config: {
|
||||
segmentIndex?: number;
|
||||
toolGroupId?: string;
|
||||
properties?: {
|
||||
label?: string;
|
||||
color?: ohifTypes.RGB;
|
||||
opacity?: number;
|
||||
visibility?: boolean;
|
||||
isLocked?: boolean;
|
||||
active?: boolean;
|
||||
};
|
||||
} = {}
|
||||
): void {
|
||||
if (segmentIndex === 0) {
|
||||
if (config?.segmentIndex === 0) {
|
||||
throw new Error('Segment index 0 is reserved for "no label"');
|
||||
}
|
||||
|
||||
toolGroupId = toolGroupId ?? this._getFirstToolGroupId();
|
||||
const toolGroupId = config.toolGroupId ?? this._getApplicableToolGroupId();
|
||||
|
||||
const { segmentationRepresentationUID, segmentation } = this._getSegmentationInfo(
|
||||
segmentationId,
|
||||
toolGroupId
|
||||
);
|
||||
|
||||
let segmentIndex = config.segmentIndex;
|
||||
if (!segmentIndex) {
|
||||
// grab the next available segment index
|
||||
segmentIndex = segmentation.segments.length === 0 ? 1 : segmentation.segments.length;
|
||||
}
|
||||
|
||||
if (this._getSegmentInfo(segmentation, segmentIndex)) {
|
||||
throw new Error(`Segment ${segmentIndex} already exists`);
|
||||
}
|
||||
@@ -147,7 +154,7 @@ class SegmentationService extends PubSubService {
|
||||
);
|
||||
|
||||
segmentation.segments[segmentIndex] = {
|
||||
label: properties.label,
|
||||
label: config.properties?.label ?? `Segment ${segmentIndex}`,
|
||||
segmentIndex: segmentIndex,
|
||||
color: [rgbaColor[0], rgbaColor[1], rgbaColor[2]],
|
||||
opacity: rgbaColor[3],
|
||||
@@ -157,9 +164,12 @@ class SegmentationService extends PubSubService {
|
||||
|
||||
segmentation.segmentCount++;
|
||||
|
||||
// make the newly added segment the active segment
|
||||
this._setActiveSegment(segmentationId, segmentIndex);
|
||||
|
||||
const suppressEvents = true;
|
||||
if (properties !== undefined) {
|
||||
const { color: newColor, opacity, isLocked, visibility, active } = properties;
|
||||
if (config.properties !== undefined) {
|
||||
const { color: newColor, opacity, isLocked, visibility, active } = config.properties;
|
||||
|
||||
if (newColor !== undefined) {
|
||||
this._setSegmentColor(segmentationId, segmentIndex, newColor, toolGroupId, suppressEvents);
|
||||
@@ -179,7 +189,7 @@ class SegmentationService extends PubSubService {
|
||||
);
|
||||
}
|
||||
|
||||
if (active !== undefined) {
|
||||
if (active === true) {
|
||||
this._setActiveSegment(segmentationId, segmentIndex, suppressEvents);
|
||||
}
|
||||
|
||||
@@ -281,17 +291,21 @@ class SegmentationService extends PubSubService {
|
||||
);
|
||||
}
|
||||
|
||||
public setSegmentLockedForSegmentation(
|
||||
segmentationId: string,
|
||||
segmentIndex: number,
|
||||
isLocked: boolean
|
||||
): void {
|
||||
public setSegmentLocked(segmentationId: string, segmentIndex: number, isLocked: boolean): void {
|
||||
const suppressEvents = false;
|
||||
this._setSegmentLocked(segmentationId, segmentIndex, isLocked, suppressEvents);
|
||||
}
|
||||
|
||||
public setSegmentLabel(segmentationId: string, segmentIndex: number, segmentLabel: string): void {
|
||||
this._setSegmentLabel(segmentationId, segmentIndex, segmentLabel);
|
||||
/**
|
||||
* Toggles the locked state of a segment in a segmentation.
|
||||
* @param segmentationId - The ID of the segmentation.
|
||||
* @param segmentIndex - The index of the segment to toggle.
|
||||
*/
|
||||
public toggleSegmentLocked(segmentationId: string, segmentIndex: number): void {
|
||||
const segmentation = this.getSegmentation(segmentationId);
|
||||
const segment = this._getSegmentInfo(segmentation, segmentIndex);
|
||||
const isLocked = !segment.isLocked;
|
||||
this._setSegmentLocked(segmentationId, segmentIndex, isLocked);
|
||||
}
|
||||
|
||||
public setSegmentColor(
|
||||
@@ -347,13 +361,13 @@ class SegmentationService extends PubSubService {
|
||||
}
|
||||
|
||||
public setActiveSegmentationForToolGroup(segmentationId: string, toolGroupId?: string): void {
|
||||
toolGroupId = toolGroupId ?? this._getFirstToolGroupId();
|
||||
toolGroupId = toolGroupId ?? this._getApplicableToolGroupId();
|
||||
|
||||
const suppressEvents = false;
|
||||
this._setActiveSegmentationForToolGroup(segmentationId, toolGroupId, suppressEvents);
|
||||
}
|
||||
|
||||
public setActiveSegmentForSegmentation(segmentationId: string, segmentIndex: number): void {
|
||||
public setActiveSegment(segmentationId: string, segmentIndex: number): void {
|
||||
this._setActiveSegment(segmentationId, segmentIndex, false);
|
||||
}
|
||||
|
||||
@@ -385,6 +399,23 @@ class SegmentationService extends PubSubService {
|
||||
return segmentations && segmentations.map(m => this.segmentations[Object.keys(m)[0]]);
|
||||
}
|
||||
|
||||
public getActiveSegmentation(): Segmentation {
|
||||
const segmentations = this.getSegmentations();
|
||||
|
||||
return segmentations.find(segmentation => segmentation.isActive);
|
||||
}
|
||||
|
||||
public getActiveSegment() {
|
||||
const activeSegmentation = this.getActiveSegmentation();
|
||||
const { activeSegmentIndex, segments } = activeSegmentation;
|
||||
|
||||
if (activeSegmentIndex === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
return segments[activeSegmentIndex];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get specific segmentation by its id.
|
||||
*
|
||||
@@ -434,12 +465,14 @@ class SegmentationService extends PubSubService {
|
||||
},
|
||||
]);
|
||||
|
||||
// Define a new color LUT and associate it with this segmentation.
|
||||
// Todo: need to be generalized to accept custom color LUTs
|
||||
const newColorLUT = this.generateNewColorLUT();
|
||||
const newColorLUTIndex = this.getNextColorLUTIndex();
|
||||
|
||||
cstSegmentation.config.color.addColorLUT(newColorLUT, newColorLUTIndex);
|
||||
// if first segmentation, we can use the default colorLUT, otherwise
|
||||
// we need to generate a new one and use a new colorLUT
|
||||
const colorLUTIndex = 0;
|
||||
if (Object.keys(this.segmentations).length !== 0) {
|
||||
const newColorLUT = this.generateNewColorLUT();
|
||||
const colorLUTIndex = this.getNextColorLUTIndex();
|
||||
cstSegmentation.config.color.addColorLUT(newColorLUT, colorLUTIndex);
|
||||
}
|
||||
|
||||
this.segmentations[segmentationId] = {
|
||||
...segmentation,
|
||||
@@ -448,8 +481,8 @@ class SegmentationService extends PubSubService {
|
||||
activeSegmentIndex: segmentation.activeSegmentIndex ?? null,
|
||||
segmentCount: segmentation.segmentCount ?? 0,
|
||||
isActive: false,
|
||||
colorLUTIndex: newColorLUTIndex,
|
||||
isVisible: true,
|
||||
colorLUTIndex,
|
||||
};
|
||||
|
||||
cachedSegmentation = this.segmentations[segmentationId];
|
||||
@@ -526,7 +559,7 @@ class SegmentationService extends PubSubService {
|
||||
volumeId: segmentationId,
|
||||
targetBuffer: {
|
||||
type: 'Uint8Array',
|
||||
sharedArrayBuffer: true,
|
||||
sharedArrayBuffer: window.SharedArrayBuffer,
|
||||
},
|
||||
});
|
||||
const derivedVolumeScalarData = derivedVolume.getScalarData();
|
||||
@@ -736,6 +769,97 @@ class SegmentationService extends PubSubService {
|
||||
return this.addOrUpdateSegmentation(segmentation, suppressEvents);
|
||||
}
|
||||
|
||||
// Todo: this should not run on the main thread
|
||||
public calculateCentroids = (
|
||||
segmentationId: string,
|
||||
segmentIndex?: number
|
||||
): Map<number, { x: number; y: number; z: number; world: number[] }> => {
|
||||
const segmentation = this.getSegmentation(segmentationId);
|
||||
const volume = this.getLabelmapVolume(segmentationId);
|
||||
const { dimensions, imageData } = volume;
|
||||
const scalarData = volume.getScalarData();
|
||||
const [dimX, dimY, numFrames] = dimensions;
|
||||
const frameLength = dimX * dimY;
|
||||
|
||||
const segmentIndices = segmentIndex
|
||||
? [segmentIndex]
|
||||
: segmentation.segments
|
||||
.filter(segment => segment?.segmentIndex)
|
||||
.map(segment => segment.segmentIndex);
|
||||
|
||||
const segmentIndicesSet = new Set(segmentIndices);
|
||||
|
||||
const centroids = new Map();
|
||||
for (const index of segmentIndicesSet) {
|
||||
centroids.set(index, { x: 0, y: 0, z: 0, count: 0 });
|
||||
}
|
||||
|
||||
let voxelIndex = 0;
|
||||
for (let frame = 0; frame < numFrames; frame++) {
|
||||
for (let p = 0; p < frameLength; p++) {
|
||||
const segmentIndex = scalarData[voxelIndex++];
|
||||
if (segmentIndicesSet.has(segmentIndex)) {
|
||||
const centroid = centroids.get(segmentIndex);
|
||||
centroid.x += p % dimX;
|
||||
centroid.y += (p / dimX) | 0;
|
||||
centroid.z += frame;
|
||||
centroid.count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const result = new Map();
|
||||
for (const [index, centroid] of centroids) {
|
||||
const count = centroid.count;
|
||||
const normalizedCentroid = {
|
||||
x: centroid.x / count,
|
||||
y: centroid.y / count,
|
||||
z: centroid.z / count,
|
||||
};
|
||||
normalizedCentroid.world = imageData.indexToWorld([
|
||||
normalizedCentroid.x,
|
||||
normalizedCentroid.y,
|
||||
normalizedCentroid.z,
|
||||
]);
|
||||
result.set(index, normalizedCentroid);
|
||||
}
|
||||
|
||||
this.setCentroids(segmentationId, result);
|
||||
return result;
|
||||
};
|
||||
|
||||
private setCentroids = (
|
||||
segmentationId: string,
|
||||
centroids: Map<number, { image: number[]; world?: number[] }>
|
||||
): void => {
|
||||
const segmentation = this.getSegmentation(segmentationId);
|
||||
const imageData = this.getLabelmapVolume(segmentationId).imageData; // Assuming this method returns imageData
|
||||
|
||||
if (!segmentation.cachedStats) {
|
||||
segmentation.cachedStats = { segmentCenter: {} };
|
||||
} else if (!segmentation.cachedStats.segmentCenter) {
|
||||
segmentation.cachedStats.segmentCenter = {};
|
||||
}
|
||||
|
||||
for (const [segmentIndex, centroid] of centroids) {
|
||||
let world = centroid.world;
|
||||
|
||||
// If world coordinates are not provided, calculate them
|
||||
if (!world || world.length === 0) {
|
||||
world = imageData.indexToWorld(centroid.image);
|
||||
}
|
||||
|
||||
segmentation.cachedStats.segmentCenter[segmentIndex] = {
|
||||
center: {
|
||||
image: centroid.image,
|
||||
world: world,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
this.addOrUpdateSegmentation(segmentation, true, true);
|
||||
};
|
||||
|
||||
public jumpToSegmentCenter(
|
||||
segmentationId: string,
|
||||
segmentIndex: number,
|
||||
@@ -749,6 +873,10 @@ class SegmentationService extends PubSubService {
|
||||
const { toolGroupService } = this.servicesManager.services;
|
||||
const center = this._getSegmentCenter(segmentationId, segmentIndex);
|
||||
|
||||
if (!center?.world) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { world } = center;
|
||||
|
||||
// todo: generalize
|
||||
@@ -801,7 +929,7 @@ class SegmentationService extends PubSubService {
|
||||
}
|
||||
|
||||
const segmentation = this.getSegmentation(segmentationId);
|
||||
toolGroupId = toolGroupId ?? this._getFirstToolGroupId();
|
||||
toolGroupId = toolGroupId ?? this._getApplicableToolGroupId();
|
||||
|
||||
const segmentationRepresentation = this._getSegmentationRepresentation(
|
||||
segmentationId,
|
||||
@@ -831,6 +959,7 @@ class SegmentationService extends PubSubService {
|
||||
displaySetInstanceUID: string,
|
||||
options?: {
|
||||
segmentationId: string;
|
||||
FrameOfReferenceUID: string;
|
||||
label: string;
|
||||
}
|
||||
): Promise<string> => {
|
||||
@@ -851,7 +980,7 @@ class SegmentationService extends PubSubService {
|
||||
volumeId: segmentationId,
|
||||
targetBuffer: {
|
||||
type: 'Uint8Array',
|
||||
sharedArrayBuffer: true,
|
||||
sharedArrayBuffer: window.SharedArrayBuffer,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -865,6 +994,8 @@ class SegmentationService extends PubSubService {
|
||||
// We should set it as active by default, as it created for display
|
||||
isActive: true,
|
||||
type: representationType,
|
||||
FrameOfReferenceUID:
|
||||
options?.FrameOfReferenceUID || displaySet.instances?.[0]?.FrameOfReferenceUID,
|
||||
representationData: {
|
||||
LABELMAP: {
|
||||
volumeId: segmentationId,
|
||||
@@ -892,7 +1023,8 @@ class SegmentationService extends PubSubService {
|
||||
toolGroupId: string,
|
||||
segmentationId: string,
|
||||
hydrateSegmentation = false,
|
||||
representationType = csToolsEnums.SegmentationRepresentations.Labelmap
|
||||
representationType = csToolsEnums.SegmentationRepresentations.Labelmap,
|
||||
suppressEvents = false
|
||||
): Promise<void> => {
|
||||
const segmentation = this.getSegmentation(segmentationId);
|
||||
|
||||
@@ -959,13 +1091,19 @@ class SegmentationService extends PubSubService {
|
||||
);
|
||||
}
|
||||
|
||||
if (isLocked !== undefined) {
|
||||
if (isLocked) {
|
||||
this._setSegmentLocked(segmentationId, segmentIndex, isLocked, suppressEvents);
|
||||
}
|
||||
}
|
||||
|
||||
if (!suppressEvents) {
|
||||
this._broadcastEvent(this.EVENTS.SEGMENTATION_UPDATED, {
|
||||
segmentation,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
public setSegmentRGBAColorForSegmentation = (
|
||||
public setSegmentRGBAColor = (
|
||||
segmentationId: string,
|
||||
segmentIndex: number,
|
||||
rgbaColor,
|
||||
@@ -1008,11 +1146,11 @@ class SegmentationService extends PubSubService {
|
||||
if (!segmentation) {
|
||||
throw new Error(`Segmentation with segmentationId ${segmentationId} not found.`);
|
||||
}
|
||||
|
||||
this._setDisplaySetIsHydrated(segmentationId, true);
|
||||
|
||||
segmentation.hydrated = true;
|
||||
|
||||
// Not all segmentations have dipslaysets, some of them are derived in the client
|
||||
this._setDisplaySetIsHydrated(segmentationId, true);
|
||||
|
||||
if (!suppressEvents) {
|
||||
this._broadcastEvent(this.EVENTS.SEGMENTATION_UPDATED, {
|
||||
segmentation,
|
||||
@@ -1021,8 +1159,13 @@ class SegmentationService extends PubSubService {
|
||||
};
|
||||
|
||||
private _setDisplaySetIsHydrated(displaySetUID: string, isHydrated: boolean): void {
|
||||
const { DisplaySetService: displaySetService } = this.servicesManager.services;
|
||||
const { displaySetService } = this.servicesManager.services;
|
||||
const displaySet = displaySetService.getDisplaySetByUID(displaySetUID);
|
||||
|
||||
if (!displaySet) {
|
||||
return;
|
||||
}
|
||||
|
||||
displaySet.isHydrated = isHydrated;
|
||||
displaySetService.setDisplaySetMetadataInvalidated(displaySetUID, false);
|
||||
}
|
||||
@@ -1167,7 +1310,6 @@ class SegmentationService extends PubSubService {
|
||||
}
|
||||
|
||||
const { colorLUTIndex } = segmentation;
|
||||
|
||||
this._removeSegmentationFromCornerstone(segmentationId);
|
||||
|
||||
// Delete associated colormap
|
||||
@@ -1181,10 +1323,14 @@ class SegmentationService extends PubSubService {
|
||||
if (wasActive) {
|
||||
const remainingSegmentations = this._getSegmentations();
|
||||
|
||||
if (remainingSegmentations.length) {
|
||||
const { id } = remainingSegmentations[0];
|
||||
const remainingHydratedSegmentations = remainingSegmentations.filter(
|
||||
segmentation => segmentation.hydrated
|
||||
);
|
||||
|
||||
this._setActiveSegmentationForToolGroup(id, this._getFirstToolGroupId(), false);
|
||||
if (remainingHydratedSegmentations.length) {
|
||||
const { id } = remainingHydratedSegmentations[0];
|
||||
|
||||
this._setActiveSegmentationForToolGroup(id, this._getApplicableToolGroupId(), false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1196,7 +1342,7 @@ class SegmentationService extends PubSubService {
|
||||
}
|
||||
|
||||
public getConfiguration = (toolGroupId?: string): SegmentationConfig => {
|
||||
toolGroupId = toolGroupId ?? this._getFirstToolGroupId();
|
||||
toolGroupId = toolGroupId ?? this._getApplicableToolGroupId();
|
||||
|
||||
const brushSize = 1;
|
||||
// const brushSize = cstUtils.segmentation.getBrushSizeForToolGroup(
|
||||
@@ -1312,15 +1458,11 @@ class SegmentationService extends PubSubService {
|
||||
return cstSegmentation.state.getSegmentationRepresentations(toolGroupId);
|
||||
};
|
||||
|
||||
public setSegmentLabelForSegmentation(
|
||||
segmentationId: string,
|
||||
segmentIndex: number,
|
||||
label: string
|
||||
) {
|
||||
this._setSegmentLabelForSegmentation(segmentationId, segmentIndex, label);
|
||||
public setSegmentLabel(segmentationId: string, segmentIndex: number, label: string) {
|
||||
this._setSegmentLabel(segmentationId, segmentIndex, label);
|
||||
}
|
||||
|
||||
private _setSegmentLabelForSegmentation(
|
||||
private _setSegmentLabel(
|
||||
segmentationId: string,
|
||||
segmentIndex: number,
|
||||
label: string,
|
||||
@@ -1348,8 +1490,8 @@ class SegmentationService extends PubSubService {
|
||||
}
|
||||
}
|
||||
|
||||
public shouldRenderSegmentation(viewportDisplaySetInstanceUIDs, segDisplaySetInstanceUID) {
|
||||
if (!viewportDisplaySetInstanceUIDs || !viewportDisplaySetInstanceUIDs.length) {
|
||||
public shouldRenderSegmentation(viewportDisplaySetInstanceUIDs, segmentationFrameOfReferenceUID) {
|
||||
if (!viewportDisplaySetInstanceUIDs?.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1357,10 +1499,6 @@ class SegmentationService extends PubSubService {
|
||||
|
||||
let shouldDisplaySeg = false;
|
||||
|
||||
const segDisplaySet = displaySetService.getDisplaySetByUID(segDisplaySetInstanceUID);
|
||||
|
||||
const segFrameOfReferenceUID = this._getFrameOfReferenceUIDForSeg(segDisplaySet);
|
||||
|
||||
// check if the displaySet is sharing the same frameOfReferenceUID
|
||||
// with the new segmentation
|
||||
for (const displaySetInstanceUID of viewportDisplaySetInstanceUIDs) {
|
||||
@@ -1370,7 +1508,7 @@ class SegmentationService extends PubSubService {
|
||||
// don't want to show the segmentation for all the frames
|
||||
if (
|
||||
displaySet.isReconstructable &&
|
||||
displaySet?.images?.[0]?.FrameOfReferenceUID === segFrameOfReferenceUID
|
||||
displaySet?.images?.[0]?.FrameOfReferenceUID === segmentationFrameOfReferenceUID
|
||||
) {
|
||||
shouldDisplaySeg = true;
|
||||
break;
|
||||
@@ -1499,7 +1637,7 @@ class SegmentationService extends PubSubService {
|
||||
throw new Error(`Segment ${segmentIndex} not yet added to segmentation: ${segmentationId}`);
|
||||
}
|
||||
|
||||
toolGroupId = toolGroupId ?? this._getFirstToolGroupId();
|
||||
toolGroupId = toolGroupId ?? this._getApplicableToolGroupId();
|
||||
|
||||
const segmentationRepresentation = this._getSegmentationRepresentation(
|
||||
segmentationId,
|
||||
@@ -1593,7 +1731,7 @@ class SegmentationService extends PubSubService {
|
||||
toolGroupId?: string,
|
||||
suppressEvents = false
|
||||
) {
|
||||
toolGroupId = toolGroupId ?? this._getFirstToolGroupId();
|
||||
toolGroupId = toolGroupId ?? this._getApplicableToolGroupId();
|
||||
|
||||
const { segmentationRepresentationUID, segmentation } = this._getSegmentationInfo(
|
||||
segmentationId,
|
||||
@@ -1652,7 +1790,7 @@ class SegmentationService extends PubSubService {
|
||||
throw new Error(`Segment ${segmentIndex} not yet added to segmentation: ${segmentationId}`);
|
||||
}
|
||||
|
||||
toolGroupId = toolGroupId ?? this._getFirstToolGroupId();
|
||||
toolGroupId = toolGroupId ?? this._getApplicableToolGroupId();
|
||||
|
||||
const segmentationRepresentation = this._getSegmentationRepresentation(
|
||||
segmentationId,
|
||||
@@ -1717,7 +1855,7 @@ class SegmentationService extends PubSubService {
|
||||
const segmentationRepresentations =
|
||||
this.getSegmentationRepresentationsForToolGroup(toolGroupId);
|
||||
|
||||
if (segmentationRepresentations.length === 0) {
|
||||
if (!segmentationRepresentations?.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1969,11 +2107,21 @@ class SegmentationService extends PubSubService {
|
||||
}
|
||||
}
|
||||
|
||||
private _getFirstToolGroupId = () => {
|
||||
const { toolGroupService } = this.servicesManager.services;
|
||||
const toolGroupIds = toolGroupService.getToolGroupIds();
|
||||
private _getApplicableToolGroupId = () => {
|
||||
const { toolGroupService, viewportGridService, cornerstoneViewportService } =
|
||||
this.servicesManager.services;
|
||||
|
||||
return toolGroupIds[0];
|
||||
const viewportInfo = cornerstoneViewportService.getViewportInfo(
|
||||
viewportGridService.getActiveViewportId()
|
||||
);
|
||||
|
||||
if (!viewportInfo) {
|
||||
const toolGroupIds = toolGroupService.getToolGroupIds();
|
||||
|
||||
return toolGroupIds[0];
|
||||
}
|
||||
|
||||
return viewportInfo.getToolGroupId();
|
||||
};
|
||||
|
||||
private getNextColorLUTIndex = (): number => {
|
||||
|
||||
@@ -39,6 +39,8 @@ type Segmentation = {
|
||||
isActive: boolean;
|
||||
// if the segmentation is visible in the viewer
|
||||
isVisible: boolean;
|
||||
// the frame of reference UID of the segmentation
|
||||
FrameOfReferenceUID: string;
|
||||
// the label of the segmentation
|
||||
label: string;
|
||||
// the number of segments in the segmentation
|
||||
|
||||
@@ -97,9 +97,9 @@ export default class ToolGroupService {
|
||||
}
|
||||
|
||||
public getActiveToolForViewport(viewportId: string): string {
|
||||
const toolGroup = ToolGroupManager.getToolGroupForViewport(viewportId);
|
||||
const toolGroup = this.getToolGroupForViewport(viewportId);
|
||||
if (!toolGroup) {
|
||||
return null;
|
||||
return;
|
||||
}
|
||||
|
||||
return toolGroup.getActivePrimaryMouseButtonTool();
|
||||
@@ -184,13 +184,9 @@ export default class ToolGroupService {
|
||||
this._setToolsMode(toolGroup, tools);
|
||||
}
|
||||
|
||||
public createToolGroupAndAddTools(
|
||||
toolGroupId: string,
|
||||
tools: Array<Tool>,
|
||||
configs: any = {}
|
||||
): Types.IToolGroup {
|
||||
public createToolGroupAndAddTools(toolGroupId: string, tools: Array<Tool>): Types.IToolGroup {
|
||||
const toolGroup = this.createToolGroup(toolGroupId);
|
||||
this.addToolsToToolGroup(toolGroupId, tools, configs);
|
||||
this.addToolsToToolGroup(toolGroupId, tools);
|
||||
return toolGroup;
|
||||
}
|
||||
|
||||
@@ -239,34 +235,6 @@ export default class ToolGroupService {
|
||||
toolInstance.configuration = config;
|
||||
}
|
||||
|
||||
private _getToolNames(toolGroupTools: Tools): string[] {
|
||||
const toolNames = [];
|
||||
if (toolGroupTools.active) {
|
||||
toolGroupTools.active.forEach(tool => {
|
||||
toolNames.push(tool.toolName);
|
||||
});
|
||||
}
|
||||
if (toolGroupTools.passive) {
|
||||
toolGroupTools.passive.forEach(tool => {
|
||||
toolNames.push(tool.toolName);
|
||||
});
|
||||
}
|
||||
|
||||
if (toolGroupTools.enabled) {
|
||||
toolGroupTools.enabled.forEach(tool => {
|
||||
toolNames.push(tool.toolName);
|
||||
});
|
||||
}
|
||||
|
||||
if (toolGroupTools.disabled) {
|
||||
toolGroupTools.disabled.forEach(tool => {
|
||||
toolNames.push(tool.toolName);
|
||||
});
|
||||
}
|
||||
|
||||
return toolNames;
|
||||
}
|
||||
|
||||
private _setToolsMode(toolGroup, tools) {
|
||||
const { active, passive, enabled, disabled } = tools;
|
||||
|
||||
@@ -295,17 +263,33 @@ export default class ToolGroupService {
|
||||
}
|
||||
}
|
||||
|
||||
private _addTools(toolGroup, tools, configs) {
|
||||
const toolNames = this._getToolNames(tools);
|
||||
toolNames.forEach(toolName => {
|
||||
// Initialize the toolConfig if no configuration is provided
|
||||
const toolConfig = configs[toolName] ?? {};
|
||||
private _addTools(toolGroup, tools) {
|
||||
const addTools = tools => {
|
||||
tools.forEach(({ toolName, parentTool, configuration }) => {
|
||||
if (parentTool) {
|
||||
toolGroup.addToolInstance(toolName, parentTool, {
|
||||
...configuration,
|
||||
});
|
||||
} else {
|
||||
toolGroup.addTool(toolName, { ...configuration });
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// if (volumeUID) {
|
||||
// toolConfig.volumeUID = volumeUID;
|
||||
// }
|
||||
if (tools.active) {
|
||||
addTools(tools.active);
|
||||
}
|
||||
|
||||
toolGroup.addTool(toolName, { ...toolConfig });
|
||||
});
|
||||
if (tools.passive) {
|
||||
addTools(tools.passive);
|
||||
}
|
||||
|
||||
if (tools.enabled) {
|
||||
addTools(tools.enabled);
|
||||
}
|
||||
|
||||
if (tools.disabled) {
|
||||
addTools(tools.disabled);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,11 +15,7 @@ import {
|
||||
import { utilities as csToolsUtils, Enums as csToolsEnums } from '@cornerstonejs/tools';
|
||||
import { IViewportService } from './IViewportService';
|
||||
import { RENDERING_ENGINE_ID } from './constants';
|
||||
import ViewportInfo, {
|
||||
ViewportOptions,
|
||||
DisplaySetOptions,
|
||||
PublicViewportOptions,
|
||||
} from './Viewport';
|
||||
import ViewportInfo, { DisplaySetOptions, PublicViewportOptions } from './Viewport';
|
||||
import { StackViewportData, VolumeViewportData } from '../../types/CornerstoneCacheService';
|
||||
import { Presentation, Presentations } from '../../types/Presentation';
|
||||
|
||||
@@ -253,6 +249,7 @@ class CornerstoneViewportService extends PubSubService implements IViewportServi
|
||||
const type = viewportInfo.getViewportType();
|
||||
const background = viewportInfo.getBackground();
|
||||
const orientation = viewportInfo.getOrientation();
|
||||
const displayArea = viewportInfo.getDisplayArea();
|
||||
|
||||
const viewportInput: Types.PublicViewportInput = {
|
||||
viewportId,
|
||||
@@ -261,6 +258,7 @@ class CornerstoneViewportService extends PubSubService implements IViewportServi
|
||||
defaultOptions: {
|
||||
background,
|
||||
orientation,
|
||||
displayArea,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -586,10 +584,23 @@ class CornerstoneViewportService extends PubSubService implements IViewportServi
|
||||
continue;
|
||||
}
|
||||
|
||||
// otherwise, check if the hydrated segmentations are in the same FOR
|
||||
// otherwise, check if the hydrated segmentations are in the same FrameOfReferenceUID
|
||||
// as the primary displaySet, if so add the representation (since it was not there)
|
||||
const { id: segDisplaySetInstanceUID, type } = segmentation;
|
||||
const segFrameOfReferenceUID = this._getFrameOfReferenceUID(segDisplaySetInstanceUID);
|
||||
const { id: segDisplaySetInstanceUID } = segmentation;
|
||||
let segFrameOfReferenceUID = this._getFrameOfReferenceUID(segDisplaySetInstanceUID);
|
||||
|
||||
if (!segFrameOfReferenceUID) {
|
||||
// if the segmentation displaySet does not have a FrameOfReferenceUID, we might check the
|
||||
// segmentation itself maybe it has a FrameOfReferenceUID
|
||||
const { FrameOfReferenceUID } = segmentation;
|
||||
if (FrameOfReferenceUID) {
|
||||
segFrameOfReferenceUID = FrameOfReferenceUID;
|
||||
}
|
||||
}
|
||||
|
||||
if (!segFrameOfReferenceUID) {
|
||||
return;
|
||||
}
|
||||
|
||||
let shouldDisplaySeg = false;
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@ export type ViewportOptions = {
|
||||
presentationIds?: UITypes.PresentationIds;
|
||||
orientation?: Enums.OrientationAxis;
|
||||
background?: Types.Point3;
|
||||
displayArea?: Types.DisplayArea;
|
||||
syncGroups?: SyncGroup[];
|
||||
initialImageOptions?: InitialImageOptions;
|
||||
customViewportProps?: Record<string, unknown>;
|
||||
@@ -39,6 +40,7 @@ export type PublicViewportOptions = {
|
||||
viewportId?: string;
|
||||
orientation?: Enums.OrientationAxis;
|
||||
background?: Types.Point3;
|
||||
displayArea?: Types.DisplayArea;
|
||||
syncGroups?: SyncGroup[];
|
||||
initialImageOptions?: InitialImageOptions;
|
||||
customViewportProps?: Record<string, unknown>;
|
||||
@@ -265,6 +267,10 @@ class ViewportInfo {
|
||||
return this.viewportOptions.orientation;
|
||||
}
|
||||
|
||||
public getDisplayArea(): Types.DisplayArea {
|
||||
return this.viewportOptions.displayArea;
|
||||
}
|
||||
|
||||
public getInitialImageOptions(): InitialImageOptions {
|
||||
return this.viewportOptions.initialImageOptions;
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ const setEnabledElement = (viewportId: string, element: HTMLElement, context?: s
|
||||
};
|
||||
|
||||
/**
|
||||
* Grabs the enabled element `dom` reference of an ative viewport.
|
||||
* Grabs the enabled element `dom` reference of an active viewport.
|
||||
*
|
||||
* @return {HTMLElement} Active viewport element.
|
||||
*/
|
||||
|
||||
@@ -76,11 +76,12 @@ export function onCompletedCalibrationLine(servicesManager, csToolsEvent) {
|
||||
|
||||
const adjustCalibration = newLength => {
|
||||
const spacingScale = newLength / length;
|
||||
const rowSpacing = spacingScale * currentRowPixelSpacing;
|
||||
const colSpacing = spacingScale * currentColumnPixelSpacing;
|
||||
|
||||
// trigger resize of the viewport to adjust the world/pixel mapping
|
||||
calibrateImageSpacing(imageId, viewport.getRenderingEngine(), rowSpacing, colSpacing);
|
||||
calibrateImageSpacing(imageId, viewport.getRenderingEngine(), {
|
||||
type: 'User',
|
||||
scale: 1 / spacingScale,
|
||||
});
|
||||
};
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { metaData } from '@cornerstonejs/core';
|
||||
import { VolumeViewport, metaData } from '@cornerstonejs/core';
|
||||
import { utilities } from '@cornerstonejs/core';
|
||||
import { IStackViewport, IVolumeViewport, Point3 } from '@cornerstonejs/core/dist/esm/types';
|
||||
import { AnnotationDisplayTool, drawing } from '@cornerstonejs/tools';
|
||||
import { guid } from '@ohif/core/src/utils';
|
||||
|
||||
@@ -46,6 +47,15 @@ class ImageOverlayViewerTool extends AnnotationDisplayTool {
|
||||
this._cachedOverlayMetadata = new Map();
|
||||
};
|
||||
|
||||
protected getReferencedImageId(viewport: IStackViewport | IVolumeViewport): string {
|
||||
if (viewport instanceof VolumeViewport) {
|
||||
return;
|
||||
}
|
||||
|
||||
const targetId = this.getTargetId(viewport);
|
||||
return targetId.split('imageId:')[1];
|
||||
}
|
||||
|
||||
renderAnnotation = (enabledElement, svgDrawingHelper) => {
|
||||
const { viewport } = enabledElement;
|
||||
|
||||
@@ -150,6 +160,8 @@ class ImageOverlayViewerTool extends AnnotationDisplayTool {
|
||||
let pixelData = null;
|
||||
if (overlay.pixelData.Value) {
|
||||
pixelData = overlay.pixelData.Value;
|
||||
} else if (overlay.pixelData instanceof Array) {
|
||||
pixelData = overlay.pixelData[0];
|
||||
} else if (overlay.pixelData.retrieveBulkData) {
|
||||
pixelData = await overlay.pixelData.retrieveBulkData();
|
||||
}
|
||||
|
||||
@@ -142,12 +142,16 @@ const CornerstoneViewportDownloadForm = ({
|
||||
const properties = viewport.getProperties();
|
||||
|
||||
downloadViewport.setStack([imageId]).then(() => {
|
||||
downloadViewport.setProperties(properties);
|
||||
try {
|
||||
downloadViewport.setProperties(properties);
|
||||
const newWidth = Math.min(width || image.width, MAX_TEXTURE_SIZE);
|
||||
const newHeight = Math.min(height || image.height, MAX_TEXTURE_SIZE);
|
||||
|
||||
const newWidth = Math.min(width || image.width, MAX_TEXTURE_SIZE);
|
||||
const newHeight = Math.min(height || image.height, MAX_TEXTURE_SIZE);
|
||||
|
||||
resolve({ width: newWidth, height: newHeight });
|
||||
resolve({ width: newWidth, height: newHeight });
|
||||
} catch (e) {
|
||||
// Happens on clicking the cancel button
|
||||
console.warn('Unable to set properties', e);
|
||||
}
|
||||
});
|
||||
} else if (downloadViewport instanceof VolumeViewport) {
|
||||
const actors = viewport.getActors();
|
||||
|
||||
@@ -97,7 +97,7 @@ class DicomLoaderService {
|
||||
|
||||
if (
|
||||
(!imageInstance && !nonImageInstance) ||
|
||||
!nonImageInstance.imageId.startsWith('dicomfile')
|
||||
!nonImageInstance.imageId?.startsWith('dicomfile')
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ export default function getInterleavedFrames(imageIds) {
|
||||
!prefetchQueuedFilled.currentPositionUpToMaximum
|
||||
) {
|
||||
if (!prefetchQueuedFilled.currentPositionDownToMinimum) {
|
||||
// Add imageId bellow
|
||||
// Add imageId below
|
||||
lowerImageIdIndex--;
|
||||
imageIdsToPrefetch.push({
|
||||
imageId: imageIds[lowerImageIdIndex],
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
import { log, Types } from '@ohif/core';
|
||||
import { EVENTS } from '@cornerstonejs/core';
|
||||
|
||||
const { TimingEnum } = Types;
|
||||
|
||||
const IMAGE_TIMING_KEYS = [
|
||||
TimingEnum.DISPLAY_SETS_TO_ALL_IMAGES,
|
||||
TimingEnum.DISPLAY_SETS_TO_FIRST_IMAGE,
|
||||
TimingEnum.STUDY_TO_FIRST_IMAGE,
|
||||
];
|
||||
|
||||
const imageTiming = {
|
||||
viewportsWaiting: 0,
|
||||
};
|
||||
|
||||
/**
|
||||
* Defines the initial view timing reporting.
|
||||
* This allows knowing how many viewports are waiting for initial views and
|
||||
* when the IMAGE_RENDERED gets sent out.
|
||||
* The first image rendered will fire the FIRST_IMAGE timeEnd logs, while
|
||||
* the last of the enabled viewport will fire the ALL_IMAGES timeEnd logs.
|
||||
*
|
||||
*/
|
||||
|
||||
export default function initViewTiming({ element }) {
|
||||
if (!IMAGE_TIMING_KEYS.find(key => log.timingKeys[key])) {
|
||||
return;
|
||||
}
|
||||
imageTiming.viewportsWaiting += 1;
|
||||
element.addEventListener(EVENTS.IMAGE_RENDERED, imageRenderedListener);
|
||||
}
|
||||
|
||||
function imageRenderedListener(evt) {
|
||||
if (evt.detail.viewportStatus === 'preRender') {
|
||||
return;
|
||||
}
|
||||
log.timeEnd(TimingEnum.DISPLAY_SETS_TO_FIRST_IMAGE);
|
||||
log.timeEnd(TimingEnum.STUDY_TO_FIRST_IMAGE);
|
||||
log.timeEnd(TimingEnum.SCRIPT_TO_VIEW);
|
||||
imageTiming.viewportsWaiting -= 1;
|
||||
evt.detail.element.removeEventListener(EVENTS.IMAGE_RENDERED, imageRenderedListener);
|
||||
if (!imageTiming.viewportsWaiting) {
|
||||
log.timeEnd(TimingEnum.DISPLAY_SETS_TO_ALL_IMAGES);
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
import SUPPORTED_TOOLS from './constants/supportedTools';
|
||||
import { getDisplayUnit } from './utils';
|
||||
import getSOPInstanceAttributes from './utils/getSOPInstanceAttributes';
|
||||
import { utils } from '@ohif/core';
|
||||
|
||||
@@ -181,7 +182,9 @@ function getDisplayText(mappedAnnotations, displaySet) {
|
||||
return displayText;
|
||||
}
|
||||
const roundedAngle = utils.roundNumber(angle, 2);
|
||||
displayText.push(`${roundedAngle} ${unit} (S: ${SeriesNumber}${instanceText}${frameText})`);
|
||||
displayText.push(
|
||||
`${roundedAngle} ${getDisplayUnit(unit)} (S: ${SeriesNumber}${instanceText}${frameText})`
|
||||
);
|
||||
|
||||
return displayText;
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ import { annotation } from '@cornerstonejs/tools';
|
||||
import SUPPORTED_TOOLS from './constants/supportedTools';
|
||||
import getSOPInstanceAttributes from './utils/getSOPInstanceAttributes';
|
||||
import { utils } from '@ohif/core';
|
||||
import { getDisplayUnit } from './utils';
|
||||
|
||||
const Bidirectional = {
|
||||
toAnnotation: measurement => {},
|
||||
@@ -99,8 +100,7 @@ function getMappedAnnotations(annotation, displaySetService) {
|
||||
);
|
||||
|
||||
const { SeriesNumber } = displaySet;
|
||||
const { length, width } = targetStats;
|
||||
const unit = 'mm';
|
||||
const { length, width, unit } = targetStats;
|
||||
|
||||
annotations.push({
|
||||
SeriesInstanceUID,
|
||||
@@ -130,9 +130,9 @@ function _getReport(mappedAnnotations, points, FrameOfReferenceUID) {
|
||||
values.push('Cornerstone:Bidirectional');
|
||||
|
||||
mappedAnnotations.forEach(annotation => {
|
||||
const { length, width } = annotation;
|
||||
columns.push(`Length (mm)`, `Width (mm)`);
|
||||
values.push(length, width);
|
||||
const { length, width, unit } = annotation;
|
||||
columns.push(`Length`, `Width`, 'Unit');
|
||||
values.push(length, width, unit);
|
||||
});
|
||||
|
||||
if (FrameOfReferenceUID) {
|
||||
@@ -162,7 +162,7 @@ function getDisplayText(mappedAnnotations, displaySet) {
|
||||
const displayText = [];
|
||||
|
||||
// Area is the same for all series
|
||||
const { length, width, SeriesNumber, SOPInstanceUID, frameNumber } = mappedAnnotations[0];
|
||||
const { length, width, unit, SeriesNumber, SOPInstanceUID, frameNumber } = mappedAnnotations[0];
|
||||
const roundedLength = utils.roundNumber(length, 2);
|
||||
const roundedWidth = utils.roundNumber(width, 2);
|
||||
|
||||
@@ -176,8 +176,10 @@ function getDisplayText(mappedAnnotations, displaySet) {
|
||||
const instanceText = InstanceNumber ? ` I: ${InstanceNumber}` : '';
|
||||
const frameText = displaySet.isMultiFrame ? ` F: ${frameNumber}` : '';
|
||||
|
||||
displayText.push(`L: ${roundedLength} mm (S: ${SeriesNumber}${instanceText}${frameText})`);
|
||||
displayText.push(`W: ${roundedWidth} mm`);
|
||||
displayText.push(
|
||||
`L: ${roundedLength} ${getDisplayUnit(unit)} (S: ${SeriesNumber}${instanceText}${frameText})`
|
||||
);
|
||||
displayText.push(`W: ${roundedWidth} ${getDisplayUnit(unit)}`);
|
||||
|
||||
return displayText;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import SUPPORTED_TOOLS from './constants/supportedTools';
|
||||
import { getDisplayUnit } from './utils';
|
||||
import getSOPInstanceAttributes from './utils/getSOPInstanceAttributes';
|
||||
import { utils } from '@ohif/core';
|
||||
|
||||
@@ -98,7 +99,7 @@ function getMappedAnnotations(annotation, DisplaySetService) {
|
||||
);
|
||||
|
||||
const { SeriesNumber } = displaySet;
|
||||
const { mean, stdDev, max, area, Modality, modalityUnit } = targetStats;
|
||||
const { mean, stdDev, max, area, Modality, areaUnit, modalityUnit } = targetStats;
|
||||
|
||||
annotations.push({
|
||||
SeriesInstanceUID,
|
||||
@@ -111,6 +112,7 @@ function getMappedAnnotations(annotation, DisplaySetService) {
|
||||
stdDev,
|
||||
max,
|
||||
area,
|
||||
areaUnit,
|
||||
});
|
||||
});
|
||||
|
||||
@@ -131,14 +133,14 @@ function _getReport(mappedAnnotations, points, FrameOfReferenceUID) {
|
||||
values.push('Cornerstone:CircleROI');
|
||||
|
||||
mappedAnnotations.forEach(annotation => {
|
||||
const { mean, stdDev, max, area, unit } = annotation;
|
||||
const { mean, stdDev, max, area, unit, areaUnit } = annotation;
|
||||
|
||||
if (!mean || !unit || !max || !area) {
|
||||
return;
|
||||
}
|
||||
|
||||
columns.push(`max (${unit})`, `mean (${unit})`, `std (${unit})`, `area (mm2)`);
|
||||
values.push(max, mean, stdDev, area);
|
||||
columns.push(`max (${unit})`, `mean (${unit})`, `std (${unit})`, 'Area', 'Unit');
|
||||
values.push(max, mean, stdDev, area, areaUnit);
|
||||
});
|
||||
|
||||
if (FrameOfReferenceUID) {
|
||||
@@ -168,7 +170,7 @@ function getDisplayText(mappedAnnotations, displaySet) {
|
||||
const displayText = [];
|
||||
|
||||
// Area is the same for all series
|
||||
const { area, SOPInstanceUID, frameNumber } = mappedAnnotations[0];
|
||||
const { area, SOPInstanceUID, frameNumber, areaUnit } = mappedAnnotations[0];
|
||||
|
||||
const instance = displaySet.images.find(image => image.SOPInstanceUID === SOPInstanceUID);
|
||||
|
||||
@@ -182,7 +184,7 @@ function getDisplayText(mappedAnnotations, displaySet) {
|
||||
|
||||
// Area sometimes becomes undefined if `preventHandleOutsideImage` is off.
|
||||
const roundedArea = utils.roundNumber(area || 0, 2);
|
||||
displayText.push(`${roundedArea} mm<sup>2</sup>`);
|
||||
displayText.push(`${roundedArea} ${getDisplayUnit(areaUnit)}`);
|
||||
|
||||
// Todo: we need a better UI for displaying all these information
|
||||
mappedAnnotations.forEach(mappedAnnotation => {
|
||||
@@ -191,7 +193,7 @@ function getDisplayText(mappedAnnotations, displaySet) {
|
||||
let maxStr = '';
|
||||
if (max) {
|
||||
const roundedMax = utils.roundNumber(max, 2);
|
||||
maxStr = `Max: ${roundedMax} <small>${unit}</small> `;
|
||||
maxStr = `Max: ${roundedMax} <small>${getDisplayUnit(unit)}</small> `;
|
||||
}
|
||||
|
||||
const str = `${maxStr}(S:${SeriesNumber}${instanceText}${frameText})`;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import SUPPORTED_TOOLS from './constants/supportedTools';
|
||||
import { getDisplayUnit } from './utils';
|
||||
import getSOPInstanceAttributes from './utils/getSOPInstanceAttributes';
|
||||
import { utils } from '@ohif/core';
|
||||
|
||||
@@ -181,7 +182,9 @@ function getDisplayText(mappedAnnotations, displaySet) {
|
||||
return displayText;
|
||||
}
|
||||
const roundedAngle = utils.roundNumber(angle, 2);
|
||||
displayText.push(`${roundedAngle} ${unit} (S: ${SeriesNumber}${instanceText}${frameText})`);
|
||||
displayText.push(
|
||||
`${roundedAngle} ${getDisplayUnit(unit)} (S: ${SeriesNumber}${instanceText}${frameText})`
|
||||
);
|
||||
|
||||
return displayText;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import SUPPORTED_TOOLS from './constants/supportedTools';
|
||||
import { getDisplayUnit } from './utils';
|
||||
import getSOPInstanceAttributes from './utils/getSOPInstanceAttributes';
|
||||
import { utils } from '@ohif/core';
|
||||
|
||||
@@ -98,7 +99,7 @@ function getMappedAnnotations(annotation, displaySetService) {
|
||||
);
|
||||
|
||||
const { SeriesNumber } = displaySet;
|
||||
const { mean, stdDev, max, area, Modality, modalityUnit } = targetStats;
|
||||
const { mean, stdDev, max, area, Modality, areaUnit, modalityUnit } = targetStats;
|
||||
|
||||
annotations.push({
|
||||
SeriesInstanceUID,
|
||||
@@ -107,6 +108,7 @@ function getMappedAnnotations(annotation, displaySetService) {
|
||||
frameNumber,
|
||||
Modality,
|
||||
unit: modalityUnit,
|
||||
areaUnit,
|
||||
mean,
|
||||
stdDev,
|
||||
max,
|
||||
@@ -131,14 +133,14 @@ function _getReport(mappedAnnotations, points, FrameOfReferenceUID) {
|
||||
values.push('Cornerstone:EllipticalROI');
|
||||
|
||||
mappedAnnotations.forEach(annotation => {
|
||||
const { mean, stdDev, max, area, unit } = annotation;
|
||||
const { mean, stdDev, max, area, unit, areaUnit } = annotation;
|
||||
|
||||
if (!mean || !unit || !max || !area) {
|
||||
return;
|
||||
}
|
||||
|
||||
columns.push(`max (${unit})`, `mean (${unit})`, `std (${unit})`, `area (mm2)`);
|
||||
values.push(max, mean, stdDev, area);
|
||||
columns.push(`max (${unit})`, `mean (${unit})`, `std (${unit})`, 'Area', 'Unit');
|
||||
values.push(max, mean, stdDev, area, areaUnit);
|
||||
});
|
||||
|
||||
if (FrameOfReferenceUID) {
|
||||
@@ -168,7 +170,7 @@ function getDisplayText(mappedAnnotations, displaySet) {
|
||||
const displayText = [];
|
||||
|
||||
// Area is the same for all series
|
||||
const { area, SOPInstanceUID, frameNumber } = mappedAnnotations[0];
|
||||
const { area, SOPInstanceUID, frameNumber, areaUnit } = mappedAnnotations[0];
|
||||
|
||||
const instance = displaySet.images.find(image => image.SOPInstanceUID === SOPInstanceUID);
|
||||
|
||||
@@ -180,9 +182,8 @@ function getDisplayText(mappedAnnotations, displaySet) {
|
||||
const instanceText = InstanceNumber ? ` I: ${InstanceNumber}` : '';
|
||||
const frameText = displaySet.isMultiFrame ? ` F: ${frameNumber}` : '';
|
||||
|
||||
// Area sometimes becomes undefined if `preventHandleOutsideImage` is off.
|
||||
const roundedArea = utils.roundNumber(area || 0, 2);
|
||||
displayText.push(`${roundedArea} mm<sup>2</sup>`);
|
||||
const roundedArea = utils.roundNumber(area, 2);
|
||||
displayText.push(`${roundedArea} ${getDisplayUnit(areaUnit)}`);
|
||||
|
||||
// Todo: we need a better UI for displaying all these information
|
||||
mappedAnnotations.forEach(mappedAnnotation => {
|
||||
@@ -191,7 +192,7 @@ function getDisplayText(mappedAnnotations, displaySet) {
|
||||
let maxStr = '';
|
||||
if (max) {
|
||||
const roundedMax = utils.roundNumber(max, 2);
|
||||
maxStr = `Max: ${roundedMax} <small>${unit}</small> `;
|
||||
maxStr = `Max: ${roundedMax} <small>${getDisplayUnit(unit)}</small> `;
|
||||
}
|
||||
|
||||
const str = `${maxStr}(S:${SeriesNumber}${instanceText}${frameText})`;
|
||||
|
||||
@@ -32,7 +32,11 @@ const Length = {
|
||||
throw new Error('Tool not supported');
|
||||
}
|
||||
|
||||
const { SOPInstanceUID, SeriesInstanceUID, StudyInstanceUID } = getSOPInstanceAttributes(
|
||||
const {
|
||||
SOPInstanceUID,
|
||||
SeriesInstanceUID,
|
||||
StudyInstanceUID,
|
||||
} = getSOPInstanceAttributes(
|
||||
referencedImageId,
|
||||
cornerstoneViewportService,
|
||||
viewportId
|
||||
@@ -94,8 +98,11 @@ function getMappedAnnotations(annotation, displaySetService) {
|
||||
throw new Error('Non-acquisition plane measurement mapping not supported');
|
||||
}
|
||||
|
||||
const { SOPInstanceUID, SeriesInstanceUID, frameNumber } =
|
||||
getSOPInstanceAttributes(referencedImageId);
|
||||
const {
|
||||
SOPInstanceUID,
|
||||
SeriesInstanceUID,
|
||||
frameNumber,
|
||||
} = getSOPInstanceAttributes(referencedImageId);
|
||||
|
||||
const displaySet = displaySetService.getDisplaySetForSOPInstanceUID(
|
||||
SOPInstanceUID,
|
||||
@@ -104,8 +111,7 @@ function getMappedAnnotations(annotation, displaySetService) {
|
||||
);
|
||||
|
||||
const { SeriesNumber } = displaySet;
|
||||
const { length } = targetStats;
|
||||
const unit = 'mm';
|
||||
const { length, unit = 'mm' } = targetStats;
|
||||
|
||||
annotations.push({
|
||||
SeriesInstanceUID,
|
||||
@@ -134,9 +140,11 @@ function _getReport(mappedAnnotations, points, FrameOfReferenceUID) {
|
||||
values.push('Cornerstone:Length');
|
||||
|
||||
mappedAnnotations.forEach(annotation => {
|
||||
const { length } = annotation;
|
||||
columns.push(`Length (mm)`);
|
||||
const { length, unit } = annotation;
|
||||
columns.push(`Length`);
|
||||
values.push(length);
|
||||
columns.push('Unit');
|
||||
values.push(unit);
|
||||
});
|
||||
|
||||
if (FrameOfReferenceUID) {
|
||||
@@ -166,7 +174,13 @@ function getDisplayText(mappedAnnotations, displaySet) {
|
||||
const displayText = [];
|
||||
|
||||
// Area is the same for all series
|
||||
const { length, SeriesNumber, SOPInstanceUID, frameNumber } = mappedAnnotations[0];
|
||||
const {
|
||||
length,
|
||||
SeriesNumber,
|
||||
SOPInstanceUID,
|
||||
frameNumber,
|
||||
unit,
|
||||
} = mappedAnnotations[0];
|
||||
|
||||
const instance = displaySet.images.find(image => image.SOPInstanceUID === SOPInstanceUID);
|
||||
|
||||
@@ -182,7 +196,9 @@ function getDisplayText(mappedAnnotations, displaySet) {
|
||||
return displayText;
|
||||
}
|
||||
const roundedLength = utils.roundNumber(length, 2);
|
||||
displayText.push(`${roundedLength} mm (S: ${SeriesNumber}${instanceText}${frameText})`);
|
||||
displayText.push(
|
||||
`${roundedLength} ${unit} (S: ${SeriesNumber}${instanceText}${frameText})`
|
||||
);
|
||||
|
||||
return displayText;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import SUPPORTED_TOOLS from './constants/supportedTools';
|
||||
import { getDisplayUnit } from './utils';
|
||||
import getSOPInstanceAttributes from './utils/getSOPInstanceAttributes';
|
||||
import { utils } from '@ohif/core';
|
||||
|
||||
@@ -98,7 +99,7 @@ function getMappedAnnotations(annotation, DisplaySetService) {
|
||||
);
|
||||
|
||||
const { SeriesNumber } = displaySet;
|
||||
const { mean, stdDev, max, area, Modality, modalityUnit } = targetStats;
|
||||
const { mean, stdDev, max, area, Modality, modalityUnit, areaUnit } = targetStats;
|
||||
|
||||
annotations.push({
|
||||
SeriesInstanceUID,
|
||||
@@ -111,6 +112,7 @@ function getMappedAnnotations(annotation, DisplaySetService) {
|
||||
stdDev,
|
||||
max,
|
||||
area,
|
||||
areaUnit,
|
||||
});
|
||||
});
|
||||
|
||||
@@ -131,14 +133,14 @@ function _getReport(mappedAnnotations, points, FrameOfReferenceUID) {
|
||||
values.push('Cornerstone:RectangleROI');
|
||||
|
||||
mappedAnnotations.forEach(annotation => {
|
||||
const { mean, stdDev, max, area, unit } = annotation;
|
||||
const { mean, stdDev, max, area, unit, areaUnit } = annotation;
|
||||
|
||||
if (!mean || !unit || !max || !area) {
|
||||
return;
|
||||
}
|
||||
|
||||
columns.push(`max (${unit})`, `mean (${unit})`, `std (${unit})`, `area (mm2)`);
|
||||
values.push(max, mean, stdDev, area);
|
||||
columns.push(`Maximum`, `Mean`, `Std Dev`, 'Pixel Unit', `Area`, 'Unit');
|
||||
values.push(max, mean, stdDev, unit, area, areaUnit);
|
||||
});
|
||||
|
||||
if (FrameOfReferenceUID) {
|
||||
@@ -168,7 +170,7 @@ function getDisplayText(mappedAnnotations, displaySet) {
|
||||
const displayText = [];
|
||||
|
||||
// Area is the same for all series
|
||||
const { area, SOPInstanceUID, frameNumber } = mappedAnnotations[0];
|
||||
const { area, SOPInstanceUID, frameNumber, areaUnit } = mappedAnnotations[0];
|
||||
|
||||
const instance = displaySet.images.find(image => image.SOPInstanceUID === SOPInstanceUID);
|
||||
|
||||
@@ -182,7 +184,7 @@ function getDisplayText(mappedAnnotations, displaySet) {
|
||||
|
||||
// Area sometimes becomes undefined if `preventHandleOutsideImage` is off.
|
||||
const roundedArea = utils.roundNumber(area || 0, 2);
|
||||
displayText.push(`${roundedArea} mm<sup>2</sup>`);
|
||||
displayText.push(`${roundedArea} ${getDisplayUnit(areaUnit)}`);
|
||||
|
||||
// Todo: we need a better UI for displaying all these information
|
||||
mappedAnnotations.forEach(mappedAnnotation => {
|
||||
@@ -191,7 +193,7 @@ function getDisplayText(mappedAnnotations, displaySet) {
|
||||
let maxStr = '';
|
||||
if (max) {
|
||||
const roundedMax = utils.roundNumber(max, 2);
|
||||
maxStr = `Max: ${roundedMax} <small>${unit}</small> `;
|
||||
maxStr = `Max: ${roundedMax} <small>${getDisplayUnit(unit)}</small> `;
|
||||
}
|
||||
|
||||
const str = `${maxStr}(S:${SeriesNumber}${instanceText}${frameText})`;
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
const getDisplayUnit = unit => (unit == null ? '' : unit);
|
||||
|
||||
export default getDisplayUnit;
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
getFirstAnnotationSelected,
|
||||
} from './selection';
|
||||
import getSOPInstanceAttributes from './getSOPInstanceAttributes';
|
||||
import getDisplayUnit from './getDisplayUnit';
|
||||
|
||||
export {
|
||||
getHandlesFromPoints,
|
||||
@@ -12,4 +13,5 @@ export {
|
||||
isAnnotationSelected,
|
||||
setAnnotationSelected,
|
||||
getFirstAnnotationSelected,
|
||||
getDisplayUnit,
|
||||
};
|
||||
@@ -1,90 +1,81 @@
|
||||
import calculateViewportRegistrations from './calculateViewportRegistrations';
|
||||
const STACK_SYNC_NAME = 'stackImageSync';
|
||||
|
||||
// [ {
|
||||
// synchronizerId: string,
|
||||
// viewports: [ { viewportId: string, renderingEngineId: string, index: number } , ...]
|
||||
// ]}
|
||||
let STACK_IMAGE_SYNC_GROUPS_INFO = [];
|
||||
export default function toggleStackImageSync({
|
||||
toggledState,
|
||||
servicesManager,
|
||||
viewports: providedViewports,
|
||||
}) {
|
||||
if (!toggledState) {
|
||||
return disableSync(STACK_SYNC_NAME, servicesManager);
|
||||
}
|
||||
|
||||
export default function toggleStackImageSync({ toggledState, servicesManager, getEnabledElement }) {
|
||||
const { syncGroupService, viewportGridService, displaySetService, cornerstoneViewportService } =
|
||||
servicesManager.services;
|
||||
|
||||
if (!toggledState) {
|
||||
STACK_IMAGE_SYNC_GROUPS_INFO.forEach(syncGroupInfo => {
|
||||
const { viewports, synchronizerId } = syncGroupInfo;
|
||||
const viewports =
|
||||
providedViewports || getReconstructableStackViewports(viewportGridService, displaySetService);
|
||||
|
||||
viewports.forEach(({ viewportId, renderingEngineId }) => {
|
||||
syncGroupService.removeViewportFromSyncGroup(viewportId, renderingEngineId, synchronizerId);
|
||||
});
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
STACK_IMAGE_SYNC_GROUPS_INFO = [];
|
||||
|
||||
// create synchronization groups and add viewports
|
||||
const { viewports } = viewportGridService.getState();
|
||||
|
||||
// filter empty viewports
|
||||
const viewportsArray = Array.from(viewports.values())
|
||||
.filter(viewport => viewport.displaySetInstanceUIDs?.length)
|
||||
// filter reconstructable viewports
|
||||
.filter(viewport => {
|
||||
const { displaySetInstanceUIDs } = viewport;
|
||||
|
||||
for (const displaySetInstanceUID of displaySetInstanceUIDs) {
|
||||
const displaySet = displaySetService.getDisplaySetByUID(displaySetInstanceUID);
|
||||
|
||||
return !!displaySet?.isReconstructable;
|
||||
}
|
||||
});
|
||||
|
||||
const viewportsByOrientation = viewportsArray.reduce((acc, viewport) => {
|
||||
const { viewportId, viewportType } = viewport.viewportOptions;
|
||||
|
||||
if (viewportType !== 'stack') {
|
||||
console.warn('Viewport is not a stack, cannot sync images yet');
|
||||
return acc;
|
||||
// create synchronization group and add the viewports to it.
|
||||
viewports.forEach(gridViewport => {
|
||||
const { viewportId } = gridViewport.viewportOptions;
|
||||
const viewport = cornerstoneViewportService.getCornerstoneViewport(viewportId);
|
||||
if (!viewport) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { element } = cornerstoneViewportService.getViewportInfo(viewportId);
|
||||
const { viewport: csViewport, renderingEngineId } = getEnabledElement(element);
|
||||
const { viewPlaneNormal } = csViewport.getCamera();
|
||||
|
||||
// Should we round here? I guess so, but not sure how much precision we need
|
||||
const orientation = viewPlaneNormal.map(v => Math.round(v)).join(',');
|
||||
|
||||
if (!acc[orientation]) {
|
||||
acc[orientation] = [];
|
||||
}
|
||||
|
||||
acc[orientation].push({ viewportId, renderingEngineId });
|
||||
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
// create synchronizer for each group
|
||||
Object.values(viewportsByOrientation).map(viewports => {
|
||||
let synchronizerId = viewports.map(({ viewportId }) => viewportId).join(',');
|
||||
|
||||
synchronizerId = `imageSync_${synchronizerId}`;
|
||||
|
||||
calculateViewportRegistrations(viewports);
|
||||
|
||||
viewports.forEach(({ viewportId, renderingEngineId }) => {
|
||||
syncGroupService.addViewportToSyncGroup(viewportId, renderingEngineId, {
|
||||
type: 'stackimage',
|
||||
id: synchronizerId,
|
||||
source: true,
|
||||
target: true,
|
||||
});
|
||||
});
|
||||
|
||||
STACK_IMAGE_SYNC_GROUPS_INFO.push({
|
||||
synchronizerId,
|
||||
viewports,
|
||||
syncGroupService.addViewportToSyncGroup(viewportId, viewport.getRenderingEngine().id, {
|
||||
type: 'stackimage',
|
||||
id: STACK_SYNC_NAME,
|
||||
source: true,
|
||||
target: true,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function disableSync(syncName, servicesManager) {
|
||||
const { syncGroupService, viewportGridService, displaySetService, cornerstoneViewportService } =
|
||||
servicesManager.services;
|
||||
const viewports = getReconstructableStackViewports(viewportGridService, displaySetService);
|
||||
viewports.forEach(gridViewport => {
|
||||
const { viewportId } = gridViewport.viewportOptions;
|
||||
const viewport = cornerstoneViewportService.getCornerstoneViewport(viewportId);
|
||||
if (!viewport) {
|
||||
return;
|
||||
}
|
||||
syncGroupService.removeViewportFromSyncGroup(
|
||||
viewport.id,
|
||||
viewport.getRenderingEngine().id,
|
||||
syncName
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the consistent spacing stack viewport types, which are the ones which
|
||||
* can be navigated using the stack image sync right now.
|
||||
*/
|
||||
function getReconstructableStackViewports(viewportGridService, displaySetService) {
|
||||
let { viewports } = viewportGridService.getState();
|
||||
|
||||
viewports = [...viewports.values()];
|
||||
// filter empty viewports
|
||||
viewports = viewports.filter(
|
||||
viewport => viewport.displaySetInstanceUIDs && viewport.displaySetInstanceUIDs.length
|
||||
);
|
||||
|
||||
// filter reconstructable viewports
|
||||
viewports = viewports.filter(viewport => {
|
||||
const { displaySetInstanceUIDs } = viewport;
|
||||
|
||||
for (const displaySetInstanceUID of displaySetInstanceUIDs) {
|
||||
const displaySet = displaySetService.getDisplaySetByUID(displaySetInstanceUID);
|
||||
|
||||
// TODO - add a better test than isReconstructable
|
||||
if (displaySet && displaySet.isReconstructable) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
});
|
||||
return viewports;
|
||||
}
|
||||
@@ -3,6 +3,403 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [3.7.0](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.110...v3.7.0) (2023-10-11)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-default
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.110](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.109...v3.7.0-beta.110) (2023-10-11)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **display messages:** broken after timings ([#3719](https://github.com/OHIF/Viewers/issues/3719)) ([157b88c](https://github.com/OHIF/Viewers/commit/157b88c909d3289cb89ace731c1f9a19d40797ac))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.109](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.108...v3.7.0-beta.109) (2023-10-11)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **export:** wrong export for the tmtv RT function ([#3715](https://github.com/OHIF/Viewers/issues/3715)) ([a3f2a1a](https://github.com/OHIF/Viewers/commit/a3f2a1a7b0d16bfcc0ecddc2ab731e54c5e377c8))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.108](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.107...v3.7.0-beta.108) (2023-10-10)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-default
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.107](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.106...v3.7.0-beta.107) (2023-10-10)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-default
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.106](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.105...v3.7.0-beta.106) (2023-10-10)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-default
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.105](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.104...v3.7.0-beta.105) (2023-10-10)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-default
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.104](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.103...v3.7.0-beta.104) (2023-10-09)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-default
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.103](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.102...v3.7.0-beta.103) (2023-10-09)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-default
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.102](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.101...v3.7.0-beta.102) (2023-10-06)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-default
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.101](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.100...v3.7.0-beta.101) (2023-10-06)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **bugs:** fixing lots of bugs regarding release candidate ([#3700](https://github.com/OHIF/Viewers/issues/3700)) ([8bc12a3](https://github.com/OHIF/Viewers/commit/8bc12a37d0353160ae5ea4624dc0b244b7d59c07))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.100](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.99...v3.7.0-beta.100) (2023-10-06)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-default
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.99](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.98...v3.7.0-beta.99) (2023-10-04)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-default
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.98](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.97...v3.7.0-beta.98) (2023-10-04)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **locale:** add German translations - community PR ([#3697](https://github.com/OHIF/Viewers/issues/3697)) ([ebe8f71](https://github.com/OHIF/Viewers/commit/ebe8f71da22c1d24b58f889c5d803951e19817b6))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.97](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.96...v3.7.0-beta.97) (2023-10-04)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-default
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.96](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.95...v3.7.0-beta.96) (2023-10-04)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-default
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.95](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.94...v3.7.0-beta.95) (2023-10-04)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-default
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.94](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.93...v3.7.0-beta.94) (2023-10-03)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **debug:** Add timing information about time to first image/all images, and query time ([#3681](https://github.com/OHIF/Viewers/issues/3681)) ([108383b](https://github.com/OHIF/Viewers/commit/108383b9ef51e4bef82d9c932b9bc7aa5354e799))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.93](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.92...v3.7.0-beta.93) (2023-10-03)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-default
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.92](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.91...v3.7.0-beta.92) (2023-10-03)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-default
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.91](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.90...v3.7.0-beta.91) (2023-10-03)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-default
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.90](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.89...v3.7.0-beta.90) (2023-10-03)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-default
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.89](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.88...v3.7.0-beta.89) (2023-10-03)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **StackSync:** Miscellaneous fixes for stack image sync ([#3663](https://github.com/OHIF/Viewers/issues/3663)) ([8a335bd](https://github.com/OHIF/Viewers/commit/8a335bd03d14ba87d65d7468d93f74040aa828d9))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.88](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.87...v3.7.0-beta.88) (2023-10-03)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-default
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.87](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.86...v3.7.0-beta.87) (2023-09-29)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-default
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.86](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.85...v3.7.0-beta.86) (2023-09-29)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-default
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.85](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.84...v3.7.0-beta.85) (2023-09-26)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **toggleOneUp:** fixed one up for main tmtv layout ([#3677](https://github.com/OHIF/Viewers/issues/3677)) ([86f54d0](https://github.com/OHIF/Viewers/commit/86f54d0d07042750a863ae876aa8dd5fb16029a5))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.84](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.83...v3.7.0-beta.84) (2023-09-26)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-default
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.83](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.82...v3.7.0-beta.83) (2023-09-26)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-default
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.82](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.81...v3.7.0-beta.82) (2023-09-26)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-default
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.81](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.80...v3.7.0-beta.81) (2023-09-26)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-default
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.80](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.79...v3.7.0-beta.80) (2023-09-22)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **segmentation mode:** Add create, and export SEG with Brushes ([#3632](https://github.com/OHIF/Viewers/issues/3632)) ([48bbd62](https://github.com/OHIF/Viewers/commit/48bbd6281a497ea68670239f5426a10ee6c56dc1))
|
||||
* **SidePanel:** new side panel tab look-and-feel ([#3657](https://github.com/OHIF/Viewers/issues/3657)) ([85c899b](https://github.com/OHIF/Viewers/commit/85c899b399e2521480724be145538993721b9378))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.79](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.78...v3.7.0-beta.79) (2023-09-22)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-default
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.78](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.77...v3.7.0-beta.78) (2023-09-21)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-default
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.77](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.76...v3.7.0-beta.77) (2023-09-21)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-default
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.76](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.75...v3.7.0-beta.76) (2023-09-19)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-default
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.75](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.74...v3.7.0-beta.75) (2023-09-18)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **DicomJson:** retrieve.series.metadata method should be async ([#3659](https://github.com/OHIF/Viewers/issues/3659)) ([2737903](https://github.com/OHIF/Viewers/commit/2737903386cf97399473e0fa64fe53ad14da155a))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.74](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.73...v3.7.0-beta.74) (2023-09-15)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-default
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.73](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.72...v3.7.0-beta.73) (2023-09-12)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **health imaging:** studies not loading from healthimaging if imagepositionpatient is missing ([#3646](https://github.com/OHIF/Viewers/issues/3646)) ([74e62a1](https://github.com/OHIF/Viewers/commit/74e62a176374f720080d4e777972f70e7f2d8b2b))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.72](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.71...v3.7.0-beta.72) (2023-09-12)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-default
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.71](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.70...v3.7.0-beta.71) (2023-09-12)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **suv:** import calculate-suv library version that prevents SUV calculation for a zero PatientWeight ([#3638](https://github.com/OHIF/Viewers/issues/3638)) ([0d10f46](https://github.com/OHIF/Viewers/commit/0d10f46b885fe54ec3dae1848134da658eb6280a))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.70](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.69...v3.7.0-beta.70) (2023-09-12)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-default
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.69](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.68...v3.7.0-beta.69) (2023-09-11)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-default
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.68](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.67...v3.7.0-beta.68) (2023-09-11)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-default
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.67](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.66...v3.7.0-beta.67) (2023-09-06)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **hotkeys:** preserve hotkeys if changed, and reduce re-rendering ([#3635](https://github.com/OHIF/Viewers/issues/3635)) ([94f7cfb](https://github.com/OHIF/Viewers/commit/94f7cfb08e3490488394efc42ef089ebe55e86be))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.7.0-beta.66](https://github.com/OHIF/Viewers/compare/v3.7.0-beta.65...v3.7.0-beta.66) (2023-09-06)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-default
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@ohif/extension-default",
|
||||
"version": "3.7.0-beta.66",
|
||||
"version": "3.7.0",
|
||||
"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.66",
|
||||
"@ohif/i18n": "3.7.0-beta.66",
|
||||
"@ohif/core": "3.7.0",
|
||||
"@ohif/i18n": "3.7.0",
|
||||
"dcmjs": "^0.29.5",
|
||||
"dicomweb-client": "^0.10.2",
|
||||
"prop-types": "^15.6.2",
|
||||
@@ -44,6 +44,6 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.20.13",
|
||||
"@cornerstonejs/calculate-suv": "^1.0.3"
|
||||
"@cornerstonejs/calculate-suv": "^1.1.0"
|
||||
}
|
||||
}
|
||||
@@ -4,49 +4,31 @@ import { DicomMetadataStore } from '@ohif/core';
|
||||
/**
|
||||
*
|
||||
* @param {*} servicesManager
|
||||
* @param {*} dataSource
|
||||
* @param {*} measurements
|
||||
* @param {*} options
|
||||
* @returns {string[]} displaySetInstanceUIDs
|
||||
*/
|
||||
async function createReportAsync(
|
||||
servicesManager,
|
||||
commandsManager,
|
||||
dataSource,
|
||||
measurements,
|
||||
options
|
||||
) {
|
||||
async function createReportAsync({ servicesManager, getReport, reportType = 'measurement' }) {
|
||||
const { displaySetService, uiNotificationService, uiDialogService } = servicesManager.services;
|
||||
const loadingDialogId = uiDialogService.create({
|
||||
showOverlay: true,
|
||||
isDraggable: false,
|
||||
centralize: true,
|
||||
// TODO: Create a loading indicator component + zeplin design?
|
||||
content: Loading,
|
||||
});
|
||||
|
||||
try {
|
||||
const naturalizedReport = await commandsManager.runCommand(
|
||||
'storeMeasurements',
|
||||
{
|
||||
measurementData: measurements,
|
||||
dataSource,
|
||||
additionalFindingTypes: ['ArrowAnnotate'],
|
||||
options,
|
||||
},
|
||||
'CORNERSTONE_STRUCTURED_REPORT'
|
||||
);
|
||||
const naturalizedReport = await getReport();
|
||||
|
||||
// The "Mode" route listens for DicomMetadataStore changes
|
||||
// When a new instance is added, it listens and
|
||||
// automatically calls makeDisplaySets
|
||||
DicomMetadataStore.addInstances([naturalizedReport], true);
|
||||
|
||||
const displaySetInstanceUID = displaySetService.getMostRecentDisplaySet();
|
||||
const displaySet = displaySetService.getMostRecentDisplaySet();
|
||||
|
||||
const displaySetInstanceUID = displaySet.displaySetInstanceUID;
|
||||
|
||||
uiNotificationService.show({
|
||||
title: 'Create Report',
|
||||
message: 'Measurements saved successfully',
|
||||
message: `${reportType} saved successfully`,
|
||||
type: 'success',
|
||||
});
|
||||
|
||||
@@ -54,7 +36,7 @@ async function createReportAsync(
|
||||
} catch (error) {
|
||||
uiNotificationService.show({
|
||||
title: 'Create Report',
|
||||
message: error.message || 'Failed to store measurements',
|
||||
message: error.message || `Failed to store ${reportType}`,
|
||||
type: 'error',
|
||||
});
|
||||
} finally {
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { SidePanel } from '@ohif/ui';
|
||||
import { PanelService, ServicesManager } from '@ohif/core';
|
||||
|
||||
export type SidePanelWithServicesProps = {
|
||||
servicesManager: ServicesManager;
|
||||
side: 'left' | 'right';
|
||||
className: string;
|
||||
activeTabIndex: number;
|
||||
tabs: any;
|
||||
};
|
||||
|
||||
const SidePanelWithServices = ({
|
||||
servicesManager,
|
||||
side,
|
||||
className,
|
||||
activeTabIndex: activeTabIndexProp,
|
||||
tabs,
|
||||
}) => {
|
||||
const panelService: PanelService = servicesManager?.services?.panelService;
|
||||
|
||||
// Tracks whether this SidePanel has been opened at least once since this SidePanel was inserted into the DOM.
|
||||
// Thus going to the Study List page and back to the viewer resets this flag for a SidePanel.
|
||||
const [hasBeenOpened, setHasBeenOpened] = useState(false);
|
||||
const [activeTabIndex, setActiveTabIndex] = useState(activeTabIndexProp);
|
||||
|
||||
useEffect(() => {
|
||||
if (panelService) {
|
||||
const activatePanelSubscription = panelService.subscribe(
|
||||
panelService.EVENTS.ACTIVATE_PANEL,
|
||||
(activatePanelEvent: Types.ActivatePanelEvent) => {
|
||||
if (!hasBeenOpened || activatePanelEvent.forceActive) {
|
||||
const tabIndex = tabs.findIndex(tab => tab.id === activatePanelEvent.panelId);
|
||||
if (tabIndex !== -1) {
|
||||
setActiveTabIndex(tabIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
return () => {
|
||||
activatePanelSubscription.unsubscribe();
|
||||
};
|
||||
}
|
||||
}, [tabs, hasBeenOpened, panelService]);
|
||||
|
||||
return (
|
||||
<SidePanel
|
||||
side={side}
|
||||
className={className}
|
||||
activeTabIndex={activeTabIndex}
|
||||
tabs={tabs}
|
||||
onOpen={() => {
|
||||
setHasBeenOpened(true);
|
||||
}}
|
||||
></SidePanel>
|
||||
);
|
||||
};
|
||||
|
||||
export default SidePanelWithServices;
|
||||
@@ -69,8 +69,8 @@ export default class ContextMenuController {
|
||||
event,
|
||||
content: ContextMenu,
|
||||
|
||||
// This naming is part of hte uiDialogService convention
|
||||
// Clicking outside simpy closes the dialog box.
|
||||
// This naming is part of the uiDialogService convention
|
||||
// Clicking outside simply closes the dialog box.
|
||||
onClickOutside: () => this.services.uiDialogService.dismiss({ id: 'context-menu' }),
|
||||
|
||||
contentProps: {
|
||||
|
||||
@@ -5,7 +5,7 @@ import { Types } from '@ohif/core';
|
||||
* menu item for display.
|
||||
* An instance of SelectorProps is provided to the selector functions, which
|
||||
* return true to include the item or false to exclude it.
|
||||
* The point of this is to allow more specific conext menus which hide
|
||||
* The point of this is to allow more specific context menus which hide
|
||||
* non-relevant menu options, optimizing the speed of selection of menus
|
||||
*/
|
||||
export interface SelectorProps {
|
||||
@@ -69,7 +69,7 @@ export interface MenuItem {
|
||||
// or more importantly, if the delegating subMenu will be included.
|
||||
selector?: (props: SelectorProps) => boolean;
|
||||
|
||||
/** Adapts the item by filling in additional properties as requried */
|
||||
/** Adapts the item by filling in additional properties as required */
|
||||
adaptItem?: (item: MenuItem, props: ContextMenuProps) => UIMenuItem;
|
||||
|
||||
/** List of commands to run when this item's action is taken. */
|
||||
|
||||
@@ -119,18 +119,18 @@ function createDicomJSONApi(dicomJsonConfig) {
|
||||
});
|
||||
},
|
||||
processResults: () => {
|
||||
console.debug(' DICOMJson QUERY processResults');
|
||||
console.warn(' DICOMJson QUERY processResults not implemented');
|
||||
},
|
||||
},
|
||||
series: {
|
||||
// mapParams: mapParams.bind(),
|
||||
search: () => {
|
||||
console.debug(' DICOMJson QUERY SERIES SEARCH');
|
||||
console.warn(' DICOMJson QUERY SERIES SEARCH not implemented');
|
||||
},
|
||||
},
|
||||
instances: {
|
||||
search: () => {
|
||||
console.debug(' DICOMJson QUERY instances SEARCH');
|
||||
console.warn(' DICOMJson QUERY instances SEARCH not implemented');
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -152,7 +152,7 @@ function createDicomJSONApi(dicomJsonConfig) {
|
||||
return getDirectURL(wadoRoot, params);
|
||||
},
|
||||
series: {
|
||||
metadata: ({ StudyInstanceUID, madeInClient = false, customSort } = {}) => {
|
||||
metadata: async ({ StudyInstanceUID, madeInClient = false, customSort } = {}) => {
|
||||
if (!StudyInstanceUID) {
|
||||
throw new Error('Unable to query for SeriesMetadata without StudyInstanceUID');
|
||||
}
|
||||
@@ -211,7 +211,7 @@ function createDicomJSONApi(dicomJsonConfig) {
|
||||
},
|
||||
store: {
|
||||
dicom: () => {
|
||||
console.debug(' DICOMJson store dicom');
|
||||
console.warn(' DICOMJson store dicom not implemented');
|
||||
},
|
||||
},
|
||||
getImageIdsForDisplaySet(displaySet) {
|
||||
|
||||
@@ -85,7 +85,7 @@ function createDicomLocalApi(dicomLocalConfig) {
|
||||
});
|
||||
},
|
||||
processResults: () => {
|
||||
console.debug(' DICOMLocal QUERY processResults');
|
||||
console.warn(' DICOMLocal QUERY processResults not implemented');
|
||||
},
|
||||
},
|
||||
series: {
|
||||
@@ -107,7 +107,7 @@ function createDicomLocalApi(dicomLocalConfig) {
|
||||
},
|
||||
instances: {
|
||||
search: () => {
|
||||
console.debug(' DICOMLocal QUERY instances SEARCH');
|
||||
console.warn(' DICOMLocal QUERY instances SEARCH not implemented');
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -221,7 +221,7 @@ function createDicomWebApi(dicomWebConfig, userAuthenticationService) {
|
||||
await wadoDicomWebClient.storeInstances(options);
|
||||
} else {
|
||||
const meta = {
|
||||
FileMetaInformationVersion: dataset._meta.FileMetaInformationVersion.Value,
|
||||
FileMetaInformationVersion: dataset._meta?.FileMetaInformationVersion?.Value,
|
||||
MediaStorageSOPClassUID: dataset.SOPClassUID,
|
||||
MediaStorageSOPInstanceUID: dataset.SOPInstanceUID,
|
||||
TransferSyntaxUID: EXPLICIT_VR_LITTLE_ENDIAN,
|
||||
@@ -295,6 +295,8 @@ function createDicomWebApi(dicomWebConfig, userAuthenticationService) {
|
||||
});
|
||||
|
||||
instance.imageId = imageId;
|
||||
instance.wadoRoot = dicomWebConfig.wadoRoot;
|
||||
instance.wadoUri = dicomWebConfig.wadoUri;
|
||||
|
||||
metadataProvider.addImageIdToUIDs(imageId, {
|
||||
StudyInstanceUID,
|
||||
@@ -343,7 +345,7 @@ function createDicomWebApi(dicomWebConfig, userAuthenticationService) {
|
||||
const addRetrieveBulkData = instance => {
|
||||
const naturalized = naturalizeDataset(instance);
|
||||
|
||||
// if we konw the server doesn't use bulkDataURI, then don't
|
||||
// if we know the server doesn't use bulkDataURI, then don't
|
||||
if (!dicomWebConfig.bulkDataURI?.enabled) {
|
||||
return naturalized;
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ const StudyMetaDataPromises = new Map();
|
||||
*
|
||||
* @param {Object} server Object with server configuration parameters
|
||||
* @param {string} StudyInstanceUID The UID of the Study to be retrieved
|
||||
* @param {boolean} enabledStudyLazyLoad Whether the study metadata should be loaded asynchronusly.
|
||||
* @param {boolean} enabledStudyLazyLoad Whether the study metadata should be loaded asynchronously.
|
||||
* @param {function} storeInstancesCallback A callback used to store the retrieved instance metadata.
|
||||
* @param {Object} [filters] - Object containing filters to be applied on retrieve metadata process
|
||||
* @param {string} [filter.seriesInstanceUID] - series instance uid to filter results against
|
||||
|
||||
@@ -160,7 +160,7 @@ export default class StaticWadoClient extends api.DICOMwebClient {
|
||||
if (!valueElem) {
|
||||
return false;
|
||||
}
|
||||
if (valueElem.vr == 'DA') {
|
||||
if (valueElem.vr === 'DA' && valueElem.Value?.[0]) {
|
||||
return this.compareDateRange(testValue, valueElem.Value[0]);
|
||||
}
|
||||
const value = valueElem.Value;
|
||||
|
||||
@@ -48,7 +48,7 @@ function createDicomWebProxyApi(dicomWebProxyConfig, UserAuthenticationService)
|
||||
retrieve: {
|
||||
directURL: (...args) => dicomWebDelegate.retrieve.directURL(...args),
|
||||
series: {
|
||||
metadata: (...args) => dicomWebDelegate.retrieve.series.metadata(...args),
|
||||
metadata: async (...args) => dicomWebDelegate.retrieve.series.metadata(...args),
|
||||
},
|
||||
},
|
||||
store: {
|
||||
|
||||
@@ -2,18 +2,18 @@ import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { LegacyButton, ButtonGroup } from '@ohif/ui';
|
||||
import { LegacyButton, LegacyButtonGroup } from '@ohif/ui';
|
||||
|
||||
function ActionButtons({ onExportClick, onCreateReportClick }) {
|
||||
const { t } = useTranslation('MeasurementTable');
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<ButtonGroup
|
||||
<LegacyButtonGroup
|
||||
color="black"
|
||||
size="inherit"
|
||||
>
|
||||
{/* TODO Revisit design of ButtonGroup later - for now use LegacyButton for its children.*/}
|
||||
{/* TODO Revisit design of LegacyButtonGroup later - for now use LegacyButton for its children.*/}
|
||||
<LegacyButton
|
||||
className="px-2 py-2 text-base"
|
||||
onClick={onExportClick}
|
||||
@@ -26,7 +26,7 @@ function ActionButtons({ onExportClick, onCreateReportClick }) {
|
||||
>
|
||||
{t('Create Report')}
|
||||
</LegacyButton>
|
||||
</ButtonGroup>
|
||||
</LegacyButtonGroup>
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -99,17 +99,24 @@ export default function PanelMeasurementTable({
|
||||
? 'Research Derived Series' // default
|
||||
: promptResult.value; // provided value
|
||||
|
||||
// Re-use an existing series having the same series description to avoid
|
||||
// Reuse an existing series having the same series description to avoid
|
||||
// creating too many series instances.
|
||||
const options = findSRWithSameSeriesDescription(SeriesDescription, displaySetService);
|
||||
|
||||
return createReportAsync(
|
||||
servicesManager,
|
||||
commandsManager,
|
||||
dataSource,
|
||||
trackedMeasurements,
|
||||
options
|
||||
);
|
||||
const getReport = async () => {
|
||||
return commandsManager.runCommand(
|
||||
'storeMeasurements',
|
||||
{
|
||||
measurementData: trackedMeasurements,
|
||||
dataSource,
|
||||
additionalFindingTypes: ['ArrowAnnotate'],
|
||||
options,
|
||||
},
|
||||
'CORNERSTONE_STRUCTURED_REPORT'
|
||||
);
|
||||
};
|
||||
|
||||
return createReportAsync({ servicesManager, getReport });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -236,7 +243,7 @@ function _getMappedMeasurements(measurementService) {
|
||||
|
||||
/**
|
||||
* Map the measurements to the display text.
|
||||
* Adds finding and site inforamtion to the displayText and/or label,
|
||||
* Adds finding and site information to the displayText and/or label,
|
||||
* and provides as 'displayText' and 'label', while providing the original
|
||||
* values as baseDisplayText and baseLabel
|
||||
*/
|
||||
|
||||
@@ -33,7 +33,6 @@ function PanelStudyBrowser({
|
||||
const [studyDisplayList, setStudyDisplayList] = useState([]);
|
||||
const [displaySets, setDisplaySets] = useState([]);
|
||||
const [thumbnailImageSrcMap, setThumbnailImageSrcMap] = useState({});
|
||||
const isMounted = useRef(true);
|
||||
|
||||
const onDoubleClickThumbnailHandler = displaySetInstanceUID => {
|
||||
let updatedViewports = [];
|
||||
@@ -103,8 +102,7 @@ function PanelStudyBrowser({
|
||||
}
|
||||
|
||||
StudyInstanceUIDs.forEach(sid => fetchStudiesForPatient(sid));
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [StudyInstanceUIDs, getStudiesForPatientByMRN]);
|
||||
}, [StudyInstanceUIDs, dataSource, getStudiesForPatientByMRN, navigate]);
|
||||
|
||||
// // ~~ Initial Thumbnails
|
||||
useEffect(() => {
|
||||
@@ -116,21 +114,17 @@ function PanelStudyBrowser({
|
||||
const imageId = imageIds[Math.floor(imageIds.length / 2)];
|
||||
|
||||
// TODO: Is it okay that imageIds are not returned here for SR displaySets?
|
||||
if (imageId && !displaySet?.unsupported) {
|
||||
// When the image arrives, render it and store the result in the thumbnailImgSrcMap
|
||||
newImageSrcEntry[dSet.displaySetInstanceUID] = await getImageSrc(imageId);
|
||||
if (isMounted.current) {
|
||||
setThumbnailImageSrcMap(prevState => {
|
||||
return { ...prevState, ...newImageSrcEntry };
|
||||
});
|
||||
}
|
||||
if (!imageId || displaySet?.unsupported) {
|
||||
return;
|
||||
}
|
||||
// When the image arrives, render it and store the result in the thumbnailImgSrcMap
|
||||
newImageSrcEntry[dSet.displaySetInstanceUID] = await getImageSrc(imageId);
|
||||
|
||||
setThumbnailImageSrcMap(prevState => {
|
||||
return { ...prevState, ...newImageSrcEntry };
|
||||
});
|
||||
});
|
||||
return () => {
|
||||
isMounted.current = false;
|
||||
};
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
}, [StudyInstanceUIDs, dataSource, displaySetService, getImageSrc]);
|
||||
|
||||
// ~~ displaySets
|
||||
useEffect(() => {
|
||||
@@ -140,8 +134,7 @@ function PanelStudyBrowser({
|
||||
sortStudyInstances(mappedDisplaySets);
|
||||
|
||||
setDisplaySets(mappedDisplaySets);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [thumbnailImageSrcMap]);
|
||||
}, [StudyInstanceUIDs, thumbnailImageSrcMap, displaySetService]);
|
||||
|
||||
// ~~ subscriptions --> displaySets
|
||||
useEffect(() => {
|
||||
@@ -149,32 +142,40 @@ function PanelStudyBrowser({
|
||||
const SubscriptionDisplaySetsAdded = displaySetService.subscribe(
|
||||
displaySetService.EVENTS.DISPLAY_SETS_ADDED,
|
||||
data => {
|
||||
const { displaySetsAdded } = data;
|
||||
const { displaySetsAdded, options } = data;
|
||||
displaySetsAdded.forEach(async dSet => {
|
||||
const newImageSrcEntry = {};
|
||||
const displaySet = displaySetService.getDisplaySetByUID(dSet.displaySetInstanceUID);
|
||||
if (!displaySet?.unsupported) {
|
||||
const imageIds = dataSource.getImageIdsForDisplaySet(displaySet);
|
||||
const imageId = imageIds[Math.floor(imageIds.length / 2)];
|
||||
|
||||
// TODO: Is it okay that imageIds are not returned here for SR displaysets?
|
||||
if (imageId) {
|
||||
// When the image arrives, render it and store the result in the thumbnailImgSrcMap
|
||||
newImageSrcEntry[dSet.displaySetInstanceUID] = await getImageSrc(
|
||||
imageId,
|
||||
dSet.initialViewport
|
||||
);
|
||||
if (isMounted.current) {
|
||||
setThumbnailImageSrcMap(prevState => {
|
||||
return { ...prevState, ...newImageSrcEntry };
|
||||
});
|
||||
}
|
||||
}
|
||||
if (displaySet?.unsupported) {
|
||||
return;
|
||||
}
|
||||
|
||||
const imageIds = dataSource.getImageIdsForDisplaySet(displaySet);
|
||||
const imageId = imageIds[Math.floor(imageIds.length / 2)];
|
||||
|
||||
// TODO: Is it okay that imageIds are not returned here for SR displaysets?
|
||||
if (!imageId) {
|
||||
return;
|
||||
}
|
||||
// When the image arrives, render it and store the result in the thumbnailImgSrcMap
|
||||
newImageSrcEntry[dSet.displaySetInstanceUID] = await getImageSrc(
|
||||
imageId,
|
||||
dSet.initialViewport
|
||||
);
|
||||
|
||||
setThumbnailImageSrcMap(prevState => {
|
||||
return { ...prevState, ...newImageSrcEntry };
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
return () => {
|
||||
SubscriptionDisplaySetsAdded.unsubscribe();
|
||||
};
|
||||
}, [getImageSrc, dataSource, displaySetService]);
|
||||
|
||||
useEffect(() => {
|
||||
// TODO: Will this always hold _all_ the displaySets we care about?
|
||||
// DISPLAY_SETS_CHANGED returns `DisplaySerService.activeDisplaySets`
|
||||
const SubscriptionDisplaySetsChanged = displaySetService.subscribe(
|
||||
@@ -198,12 +199,10 @@ function PanelStudyBrowser({
|
||||
);
|
||||
|
||||
return () => {
|
||||
SubscriptionDisplaySetsAdded.unsubscribe();
|
||||
SubscriptionDisplaySetsChanged.unsubscribe();
|
||||
SubscriptionDisplaySetMetaDataInvalidated.unsubscribe();
|
||||
};
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
}, [StudyInstanceUIDs, thumbnailImageSrcMap, displaySetService]);
|
||||
|
||||
const tabs = _createStudyBrowserTabs(StudyInstanceUIDs, studyDisplayList, displaySets);
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/* eslint-disable react/display-name */
|
||||
import React from 'react';
|
||||
|
||||
import { ButtonEnums, Dialog, Input, Select } from '@ohif/ui';
|
||||
|
||||
export const CREATE_REPORT_DIALOG_RESPONSE = {
|
||||
@@ -7,7 +7,7 @@ export const CREATE_REPORT_DIALOG_RESPONSE = {
|
||||
CREATE_REPORT: 1,
|
||||
};
|
||||
|
||||
export default function createReportDialogPrompt(uiDialogService, { extensionManager }) {
|
||||
export default function CreateReportDialogPrompt(uiDialogService, { extensionManager }) {
|
||||
return new Promise(function (resolve, reject) {
|
||||
let dialogId = undefined;
|
||||
|
||||
@@ -97,33 +97,38 @@ export default function createReportDialogPrompt(uiDialogService, { extensionMan
|
||||
};
|
||||
return (
|
||||
<>
|
||||
{dataSourcesOpts.length > 1 && (
|
||||
<Select
|
||||
closeMenuOnSelect={true}
|
||||
className="border-primary-main mr-2 bg-black"
|
||||
options={dataSourcesOpts}
|
||||
placeholder={
|
||||
dataSourcesOpts.find(option => option.value === value.dataSourceName)
|
||||
.placeHolder
|
||||
}
|
||||
value={value.dataSourceName}
|
||||
onChange={evt => {
|
||||
setValue(v => ({ ...v, dataSourceName: evt.value }));
|
||||
}}
|
||||
isClearable={false}
|
||||
/>
|
||||
{dataSourcesOpts.length > 1 && window.config?.allowMultiSelectExport && (
|
||||
<div>
|
||||
<label className="text-[14px] leading-[1.2] text-white">Data Source</label>
|
||||
<Select
|
||||
closeMenuOnSelect={true}
|
||||
className="border-primary-main mt-2 bg-black"
|
||||
options={dataSourcesOpts}
|
||||
placeholder={
|
||||
dataSourcesOpts.find(option => option.value === value.dataSourceName)
|
||||
.placeHolder
|
||||
}
|
||||
value={value.dataSourceName}
|
||||
onChange={evt => {
|
||||
setValue(v => ({ ...v, dataSourceName: evt.value }));
|
||||
}}
|
||||
isClearable={false}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<Input
|
||||
autoFocus
|
||||
label="Enter the report name"
|
||||
labelClassName="text-white text-[14px] leading-[1.2]"
|
||||
className="border-primary-main bg-black"
|
||||
type="text"
|
||||
value={value.label}
|
||||
onChange={onChangeHandler}
|
||||
onKeyPress={onKeyPressHandler}
|
||||
required
|
||||
/>
|
||||
<div className="mt-3">
|
||||
<Input
|
||||
autoFocus
|
||||
label="Enter the report name"
|
||||
labelClassName="text-white text-[14px] leading-[1.2]"
|
||||
className="border-primary-main bg-black"
|
||||
type="text"
|
||||
value={value.label}
|
||||
onChange={onChangeHandler}
|
||||
onKeyPress={onKeyPressHandler}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
},
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
import PanelStudyBrowser from './PanelStudyBrowser';
|
||||
import WrappedPanelStudyBrowser from './WrappedPanelStudyBrowser';
|
||||
import PanelMeasurementTable from './PanelMeasurementTable';
|
||||
import createReportDialogPrompt from './createReportDialogPrompt';
|
||||
|
||||
export { PanelStudyBrowser, WrappedPanelStudyBrowser, PanelMeasurementTable };
|
||||
export {
|
||||
PanelStudyBrowser,
|
||||
WrappedPanelStudyBrowser,
|
||||
PanelMeasurementTable,
|
||||
createReportDialogPrompt,
|
||||
};
|
||||
@@ -1,51 +1,29 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import React, { useEffect, useState, useCallback } from 'react';
|
||||
import classnames from 'classnames';
|
||||
|
||||
export default function Toolbar({ servicesManager }) {
|
||||
const { toolbarService } = servicesManager.services;
|
||||
const [toolbarButtons, setToolbarButtons] = useState([]);
|
||||
const [buttonState, setButtonState] = useState({
|
||||
primaryToolId: '',
|
||||
toggles: {},
|
||||
groups: {},
|
||||
});
|
||||
|
||||
// Could track buttons and state separately...?
|
||||
useEffect(() => {
|
||||
const { unsubscribe: unsub1 } = toolbarService.subscribe(
|
||||
toolbarService.EVENTS.TOOL_BAR_MODIFIED,
|
||||
() => setToolbarButtons(toolbarService.getButtonSection('primary'))
|
||||
);
|
||||
const { unsubscribe: unsub2 } = toolbarService.subscribe(
|
||||
toolbarService.EVENTS.TOOL_BAR_STATE_MODIFIED,
|
||||
() => setButtonState({ ...toolbarService.state })
|
||||
const { unsubscribe } = toolbarService.subscribe(toolbarService.EVENTS.TOOL_BAR_MODIFIED, () =>
|
||||
setToolbarButtons(toolbarService.getButtonSection('primary'))
|
||||
);
|
||||
|
||||
return () => {
|
||||
unsub1();
|
||||
unsub2();
|
||||
unsubscribe();
|
||||
};
|
||||
}, [toolbarService]);
|
||||
|
||||
const onInteraction = useCallback(
|
||||
args => toolbarService.recordInteraction(args),
|
||||
[toolbarService]
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
{toolbarButtons.map(toolDef => {
|
||||
const { id, Component, componentProps } = toolDef;
|
||||
// TODO: ...
|
||||
|
||||
// isActive if:
|
||||
// - id is primary?
|
||||
// - id is in list of "toggled on"?
|
||||
let isActive;
|
||||
if (componentProps.type === 'toggle') {
|
||||
isActive = buttonState.toggles[id];
|
||||
}
|
||||
// Also need... to filter list for splitButton, and set primary based on most recently clicked
|
||||
// Also need to kill the radioGroup button's magic logic
|
||||
// Everything should be reactive off these props, so commands can inform ToolbarService
|
||||
|
||||
// These can... Trigger toolbar events based on updates?
|
||||
// Then sync using useEffect, or simply modify the state here?
|
||||
return (
|
||||
// The margin for separating the tools on the toolbar should go here and NOT in each individual component (button) item.
|
||||
// This allows for the individual items to be included in other UI components where perhaps alternative margins are desired.
|
||||
@@ -56,9 +34,7 @@ export default function Toolbar({ servicesManager }) {
|
||||
<Component
|
||||
id={id}
|
||||
{...componentProps}
|
||||
bState={buttonState}
|
||||
isActive={isActive}
|
||||
onInteraction={args => toolbarService.recordInteraction(args)}
|
||||
onInteraction={onInteraction}
|
||||
servicesManager={servicesManager}
|
||||
/>
|
||||
</div>
|
||||
|
||||
Loaded 100 of 478 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user