Compare commits

..
Author SHA1 Message Date
ohif-bot f412dc254b chore(version): Update package versions [skip ci] 2025-05-07 20:24:55 +00:00
ohif-bot c0ed128fb7 chore(version): version.json [skip ci] 2025-05-07 20:24:49 +00:00
Rafael Osipov c829270c80 feat: add locale files for Russian language, translated texts in locale json files to Russian language. (#4984) 2025-05-07 16:20:10 -04:00
ohif-bot a16d612a8a chore(version): Update package versions [skip ci] 2025-05-06 15:10:20 +00:00
ohif-bot 5f94ebe81a chore(version): version.json [skip ci] 2025-05-06 15:10:13 +00:00
Ibrahim 8218a9d034 feat(i18n): add Arabic translations (#5013) 2025-05-06 11:05:34 -04:00
ohif-bot 4b94f18f7c chore(version): Update package versions [skip ci] 2025-05-05 20:40:11 +00:00
ohif-bot 8e7e9ca02f chore(version): version.json [skip ci] 2025-05-05 20:40:04 +00:00
Alireza cd4e6408bc feat(data overlay): add foreground background control plus orientation change in the UI and add RTDOSE support (#5007) 2025-05-05 16:35:05 -04:00
ohif-bot c1fced0573 chore(version): Update package versions [skip ci] 2025-05-05 15:32:36 +00:00
ohif-bot e41019f2b3 chore(version): version.json [skip ci] 2025-05-05 15:32:30 +00:00
Alireza 383f45823c refactor(viewport): Remove resize detector and optimize viewport resizing logic (#5006) 2025-05-05 11:27:26 -04:00
ohif-bot 6337d2e9cb chore(version): Update package versions [skip ci] 2025-05-02 18:50:57 +00:00
ohif-bot e987710908 chore(version): version.json [skip ci] 2025-05-02 18:50:50 +00:00
Pedro Köhler 7dbe5e37b0 feat: improve menu modals customizations (#5011) 2025-05-02 14:45:39 -04:00
ohif-bot ff974e2a03 chore(version): Update package versions [skip ci] 2025-05-02 18:10:04 +00:00
ohif-bot cccf74d6ea chore(version): version.json [skip ci] 2025-05-02 18:09:57 +00:00
Alireza 4be8495335 fix/remove legacy ui (#5010) 2025-05-02 14:05:40 -04:00
ohif-bot 1cb8d328a4 chore(version): Update package versions [skip ci] 2025-05-01 22:22:48 +00:00
ohif-bot 2c8dda01ae chore(version): version.json [skip ci] 2025-05-01 22:22:42 +00:00
Alireza 4e185e4b21 docs: add llm txt (#5009) 2025-05-01 18:13:21 -04:00
ohif-bot 4732e8093e chore(version): Update package versions [skip ci] 2025-05-01 18:51:19 +00:00
ohif-bot 984471e972 chore(version): version.json [skip ci] 2025-05-01 18:51:13 +00:00
Julia Young 25cb71b3f6 docs: clarify terminology for study, series, and display sets (#5000) 2025-05-01 14:46:21 -04:00
ohif-bot 0e75f6d62c chore(version): Update package versions [skip ci] 2025-04-30 16:29:06 +00:00
ohif-bot d75b3e25b6 chore(version): version.json [skip ci] 2025-04-30 16:28:59 +00:00
Tang Cheng 0bb9b17636 Fix: issue #4759 Enable proper DICOM image rendering when Rescale parameters are set to null (#5003) 2025-04-30 12:24:06 -04:00
ohif-bot b21ee9bc5b chore(version): Update package versions [skip ci] 2025-04-29 18:58:50 +00:00
ohif-bot 7ed0f6d53b chore(version): version.json [skip ci] 2025-04-29 18:58:44 +00:00
Alireza 98f8187dfe fix(referencelines): and bring back the notification for worker updates but disallow duplication (#5005) 2025-04-29 14:53:37 -04:00
ohif-bot 4643d6dc40 chore(version): Update package versions [skip ci] 2025-04-29 17:02:30 +00:00
ohif-bot 258b353269 chore(version): version.json [skip ci] 2025-04-29 17:02:23 +00:00
devin-ai-integration[bot]andAlireza Sedghi 0f39496290 fix(template): Update mode template to match current implementation (OHI-1833) (#4998)
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: Alireza Sedghi <ar.sedghi@gmail.com>
2025-04-29 12:57:24 -04:00
ohif-bot 325cb3f7d5 chore(version): Update package versions [skip ci] 2025-04-22 17:33:27 +00:00
ohif-bot 088b563bf6 chore(version): version.json [skip ci] 2025-04-22 17:33:21 +00:00
Dan Rukas 09ac43f297 fix(docs): Fix SSR build errors by dynamically importing UI components and resolving other issues (#4991) 2025-04-22 13:28:49 -04:00
ohif-bot d59828ab22 chore(version): Update package versions [skip ci] 2025-04-22 14:50:19 +00:00
ohif-bot 5f04b0f3be chore(version): version.json [skip ci] 2025-04-22 14:50:12 +00:00
Alireza bdb847a6eb fix docs (#4988) 2025-04-22 10:45:14 -04:00
ohif-bot 0052bf1ae3 chore(version): Update package versions [skip ci] 2025-04-22 14:05:34 +00:00
ohif-bot 41815ee2af chore(version): version.json [skip ci] 2025-04-22 14:05:28 +00:00
Alireza 56fdb82812 fix docs (#4986) 2025-04-22 10:00:40 -04:00
ohif-bot fcde5ddbce chore(version): Update package versions [skip ci] 2025-04-22 13:20:21 +00:00
ohif-bot 7818f406d8 chore(version): version.json [skip ci] 2025-04-22 13:20:15 +00:00
Dan Rukas 329d5b6237 docs(components): Adds new components to ui-next documentation (#4974) 2025-04-22 09:15:40 -04:00
Devu-trenser 3b8faa8a76 feat: Improve unsupported displayset messages (#4979) 2025-04-22 09:11:28 -04:00
ohif-bot 59136e5435 chore(version): Update package versions [skip ci] 2025-04-18 15:38:25 +00:00
ohif-bot 88fccda491 chore(version): version.json [skip ci] 2025-04-18 15:38:19 +00:00
Pedro Köhler 5688a2b4b8 fix(dicomWebClient): use public getter for activeDataSourceName instead of accessing private property (#4973) 2025-04-18 11:33:42 -04:00
ohif-bot f02ce22ae3 chore(version): Update package versions [skip ci] 2025-04-17 20:49:39 +00:00
ohif-bot ff5a152270 chore(version): version.json [skip ci] 2025-04-17 20:49:33 +00:00
dependabot[bot] 082014a694 chore(deps): bump estree-util-value-to-estree from 3.2.1 to 3.3.3 in /platform/docs (#4929)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-17 16:43:55 -04:00
dependabot[bot] 194b4971c2 chore(deps): bump @babel/runtime from 7.26.0 to 7.27.0 in /platform/docs (#4964)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-17 16:43:48 -04:00
dependabot[bot] d6c7b9c23c chore(deps): bump http-proxy-middleware from 2.0.7 to 2.0.9 (#4963)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-17 16:43:39 -04:00
dependabot[bot] b82465344c chore(deps): bump @babel/runtime-corejs3 from 7.24.7 to 7.27.0 (#4962)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-17 16:43:30 -04:00
ohif-bot f8cb810b92 chore(version): Update package versions [skip ci] 2025-04-17 12:36:52 +00:00
ohif-bot dbda78df15 chore(version): version.json [skip ci] 2025-04-17 12:36:45 +00:00
Pedro Köhler 66a16cd22f fix(ExtensionManager): getDataSources method (#4959) 2025-04-17 08:31:12 -04:00
ohif-bot 5a9fcd4bf0 chore(version): Update package versions [skip ci] 2025-04-16 21:40:06 +00:00
ohif-bot 69cddff628 chore(version): version.json [skip ci] 2025-04-16 21:40:00 +00:00
Alireza 08c14e9ba2 fix/runner (#4971) 2025-04-16 17:35:32 -04:00
Alireza 7fd522ab68 fix/runner (#4970) 2025-04-16 16:57:59 -04:00
Alireza df74557866 fix/runner (#4969) 2025-04-16 16:49:36 -04:00
Alireza 69063a41b7 fix/runner (#4968) 2025-04-16 16:41:35 -04:00
Alireza 41fb597085 fix/runner4 (#4967) 2025-04-16 16:31:24 -04:00
ohif-bot a3a35471a9 chore(version): Update package versions [skip ci] 2025-04-16 20:21:17 +00:00
ohif-bot a0bcac15cd chore(version): version.json [skip ci] 2025-04-16 20:21:10 +00:00
Alireza ac7875128c fix/runner (#4966) 2025-04-16 16:16:28 -04:00
Alireza bd02205ef9 fix/runner (#4965) 2025-04-16 16:09:06 -04:00
Alireza 9535422361 fix: github runner (#4961) 2025-04-16 16:02:06 -04:00
ohif-bot a13380407b chore(version): Update package versions [skip ci] 2025-04-16 20:01:50 +00:00
ohif-bot 1a0d7a930c chore(version): version.json [skip ci] 2025-04-16 20:01:43 +00:00
Alireza 91117f40f8 chore: add test coverage (#4960) 2025-04-16 15:56:52 -04:00
ohif-bot a41ed3bade chore(version): Update package versions [skip ci] 2025-04-14 14:05:04 +00:00
ohif-bot f12f2bfec5 chore(version): version.json [skip ci] 2025-04-14 14:04:58 +00:00
Alireza 4a87302f06 fix: making onnxruntime work with rsbuild (#4957) 2025-04-14 09:59:39 -04:00
ohif-bot 5dae147155 chore(version): Update package versions [skip ci] 2025-04-11 19:09:38 +00:00
ohif-bot 4952dab010 chore(version): version.json [skip ci] 2025-04-11 19:09:31 +00:00
Dan Rukas 5425308e55 ui(fix): Study panel scrollbar fix and updates (#4951) 2025-04-11 15:03:56 -04:00
ohif-bot c50298f394 chore(version): Update package versions [skip ci] 2025-04-11 16:46:37 +00:00
ohif-bot a82ebeebb2 chore(version): version.json [skip ci] 2025-04-11 16:46:31 +00:00
Pedro Köhler ed94df0dfe fix: add back study browser scrollbar (#4942) 2025-04-11 12:41:16 -04:00
Pedro Köhler 4926a9532c feat: expose scroll thumbnail into view (#4941) 2025-04-11 12:41:04 -04:00
Pedro Köhler 7a36eba67c fix: enable measurements panel scroll (#4943) 2025-04-11 12:40:35 -04:00
ohif-bot 8a08d26f01 chore(version): Update package versions [skip ci] 2025-04-11 13:47:41 +00:00
ohif-bot de598cdc1f chore(version): version.json [skip ci] 2025-04-11 13:47:35 +00:00
Pedro Köhler a9b180c5d4 fix: make datasource selection work (#4944) 2025-04-11 09:42:54 -04:00
766 changed files with 13591 additions and 20343 deletions

No files matched your search

+108
View File
@@ -0,0 +1,108 @@
name: Build and Deploy Docs
on:
push:
branches: [master]
env:
ACTIONS_STEP_DEBUG: true
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
build-and-deploy-docs:
timeout-minutes: 60
runs-on: ubuntu-latest
# Need permissions to read actions and pull requests
permissions:
actions: read
contents: read
pull-requests: read
steps:
- uses: actions/checkout@v4
- uses: oven-sh/setup-bun@v2
- uses: actions/setup-node@v4
with:
node-version: 20 # Or your desired Node version
- name: Install root dependencies
run: bun install --frozen-lockfile
# Removed Playwright tests and coverage generation steps
- name: Find PR and associated workflow run
id: find_pr_run
env:
GH_TOKEN: ${{ github.token }}
MERGE_COMMIT_SHA: ${{ github.sha }}
run: |
# Find the PR associated with the merge commit SHA
# Note: This relies on the merge commit being directly pushed to main
PR_DATA=$(gh pr list --state merged --search "$MERGE_COMMIT_SHA" --json number,headRefOid --jq '.[0]')
if [ -z "$PR_DATA" ]; then
echo "Could not find merged PR for commit $MERGE_COMMIT_SHA."
# Decide how to handle - fail, or maybe generate coverage now?
# For now, let's fail.
exit 1
fi
PR_HEAD_SHA=$(echo "$PR_DATA" | jq -r '.headRefOid')
PR_NUMBER=$(echo "$PR_DATA" | jq -r '.number')
echo "Found PR Number: $PR_NUMBER"
echo "Found PR Head SHA: $PR_HEAD_SHA"
# Find the latest workflow run ID for the playwright workflow on the PR head commit
# Uses the workflow file name 'playwright.yml'
# Remove the --status success flag to find any run
RUN_ID=$(gh run list --workflow playwright.yml --commit "$PR_HEAD_SHA" --event pull_request --json databaseId --jq '.[0].databaseId')
if [ -z "$RUN_ID" ]; then
echo "Could not find any 'playwright.yml' run for PR $PR_NUMBER (Head SHA: $PR_HEAD_SHA)."
# Decide how to handle - maybe try finding the artifact from the merge commit run if that exists?
# For now, let's fail.
exit 1
fi
echo "Found Run ID: $RUN_ID"
echo "run_id=$RUN_ID" >> $GITHUB_OUTPUT
- name: Download coverage artifact from PR run
env:
GH_TOKEN: ${{ github.token }}
run: |
mkdir -p ./coverage-artifact
gh run download ${{ steps.find_pr_run.outputs.run_id }} -n coverage-report-pr --dir ./coverage-artifact
# Check if download was successful (e.g., check if files exist)
if [ ! -f ./coverage-artifact/base.css ]; then
echo "Failed to download or find expected files in artifact 'coverage-report-pr' from run ${{ steps.find_pr_run.outputs.run_id }}."
exit 1
fi
echo "Artifact downloaded successfully."
- name: Install docs dependencies
run: cd platform/docs && bun install
- name: Copy coverage to docs static directory
run: |
# Copy files from the downloaded artifact directory
mkdir -p platform/docs/static/coverage
cp -r ./coverage-artifact/* platform/docs/static/coverage/
# Copy specific asset files from the downloaded artifact root to static root
cp ./coverage-artifact/base.css platform/docs/static/
cp ./coverage-artifact/block-navigation.js platform/docs/static/
cp ./coverage-artifact/prettify.css platform/docs/static/
cp ./coverage-artifact/prettify.js platform/docs/static/
cp ./coverage-artifact/favicon.png platform/docs/static/
cp ./coverage-artifact/sort-arrow-sprite.png platform/docs/static/
cp ./coverage-artifact/sorter.js platform/docs/static/
- name: Build docs
run: cd platform/docs && bun run build
- name: Deploy to Netlify
run: |
cd platform/docs
npx netlify-cli deploy --dir=./build --prod
env:
NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE_ID }}
+98
View File
@@ -0,0 +1,98 @@
# For most projects, this workflow file will not need changing; you simply need
# to commit it to your repository.
#
# You may wish to alter this file to override the set of languages analyzed,
# or to provide custom queries or build logic.
#
# ******** NOTE ********
# We have attempted to detect the languages in your repository. Please check
# the `language` matrix defined below to confirm you have the correct set of
# supported CodeQL languages.
#
name: "CodeQL Advanced"
on:
pull_request:
branches: [ "main" ]
schedule:
- cron: '15 1 * * 5'
jobs:
analyze:
name: Analyze (${{ matrix.language }})
# Runner size impacts CodeQL analysis time. To learn more, please see:
# - https://gh.io/recommended-hardware-resources-for-running-codeql
# - https://gh.io/supported-runners-and-hardware-resources
# - https://gh.io/using-larger-runners (GitHub.com only)
# Consider using larger runners or machines with greater resources for possible analysis time improvements.
runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }}
permissions:
# required for all workflows
security-events: write
# required to fetch internal or private CodeQL packs
packages: read
# only required for workflows in private repositories
actions: read
contents: read
strategy:
fail-fast: false
matrix:
include:
- language: actions
build-mode: none
- language: javascript-typescript
build-mode: none
# CodeQL supports the following values keywords for 'language': 'actions', 'c-cpp', 'csharp', 'go', 'java-kotlin', 'javascript-typescript', 'python', 'ruby', 'swift'
# Use `c-cpp` to analyze code written in C, C++ or both
# Use 'java-kotlin' to analyze code written in Java, Kotlin or both
# Use 'javascript-typescript' to analyze code written in JavaScript, TypeScript or both
# To learn more about changing the languages that are analyzed or customizing the build mode for your analysis,
# see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/customizing-your-advanced-setup-for-code-scanning.
# If you are analyzing a compiled language, you can modify the 'build-mode' for that language to customize how
# your codebase is analyzed, see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages
steps:
- name: Checkout repository
uses: actions/checkout@v4
# Add any setup steps before running the `github/codeql-action/init` action.
# This includes steps like installing compilers or runtimes (`actions/setup-node`
# or others). This is typically only required for manual builds.
# - name: Setup runtime (example)
# uses: actions/setup-example@v1
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v3
with:
languages: ${{ matrix.language }}
build-mode: ${{ matrix.build-mode }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
# queries: security-extended,security-and-quality
# If the analyze step fails for one of the languages you are analyzing with
# "We were unable to automatically build your code", modify the matrix above
# to set the build mode to "manual" for that language. Then modify this step
# to build your code.
# ℹ️ Command-line programs to run using the OS shell.
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
- if: matrix.build-mode == 'manual'
shell: bash
run: |
echo 'If you are using a "manual" build mode for one or more of the' \
'languages you are analyzing, replace this with the commands to build' \
'your code, for example:'
echo ' make bootstrap'
echo ' make release'
exit 1
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v3
with:
category: "/language:${{matrix.language}}"
+20 -54
View File
@@ -1,75 +1,41 @@
name: Playwright Tests
on:
push:
branches: [main, master]
pull_request:
branches: [main, master]
branches: [master]
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
playwright-tests:
timeout-minutes: 60
runs-on: ubuntu-latest
container:
image: mcr.microsoft.com/playwright:v1.51.1-noble
runs-on: self-hosted
strategy:
fail-fast: false
matrix:
shardIndex: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
shardTotal: [10]
node-version: [20]
steps:
- uses: actions/checkout@v4
- name: Install unzip
run: apt-get update && apt-get install -y unzip
- uses: oven-sh/setup-bun@v2
- uses: actions/setup-node@v4
with:
node-version: 20
node-version: ${{ matrix.node-version }}
- name: Install dependencies
run: bun install --frozen-lockfile
- name: Set up Git safe directory
run: git config --global --add safe.directory /__w/Viewers/Viewers
# - name: Install Playwright browsers
# run: bun x playwright install-deps chromium
- name: Run Playwright tests
run: |
NODE_OPTIONS="--max_old_space_size=8192" TEST_ENV=true bun x playwright test --shard=${{ matrix.shardIndex }}/${{ matrix.shardTotal }}
- name: Upload blob report to GitHub Actions Artifacts
export NODE_OPTIONS="--max_old_space_size=10192"
bun run test:e2e:coverage
- name: create the coverage report
run: |
bun nyc report --reporter=lcov --reporter=text
- name: Upload the coverage report to GitHub Actions Artifacts
if: ${{ !cancelled() }}
uses: actions/upload-artifact@v4
with:
name: blob-report-${{ matrix.shardIndex }}
path: blob-report
retention-days: 1
merge-reports:
if: ${{ !cancelled() }}
needs: [playwright-tests]
runs-on: ubuntu-latest
container:
image: mcr.microsoft.com/playwright:v1.48.1-focal
steps:
- name: Install unzip
run: apt-get update && apt-get install -y unzip
- uses: oven-sh/setup-bun@v2
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
- name: Install dependencies
run: bun install --frozen-lockfile
- name: Set up Git safe directory
run: git config --global --add safe.directory /__w/Viewers/Viewers
- name: Download blob reports from GitHub Actions Artifacts
uses: actions/download-artifact@v4
with:
path: all-blob-reports
pattern: blob-report-*
merge-multiple: true
- name: Merge into HTML Report
run: bun x playwright merge-reports --reporter html ./all-blob-reports
- name: Upload HTML report
uses: actions/upload-artifact@v4
with:
name: html-report--attempt-${{ github.run_attempt }}
path: playwright-report
retention-days: 14
name: coverage-report-pr
path: coverage
retention-days: 3
+4 -1
View File
@@ -1,7 +1,7 @@
# Packages
node_modules
.cursor/
.nyc_output/
# Output
build
dist
@@ -13,6 +13,7 @@ coverage/
.yarn/
.nx/
addOns/yarn.lock
playwright-report/
# YALC (for Erik)
.yalc
@@ -57,3 +58,5 @@ tests/test-results/
tests/playwright-report/
/blob-report/
/playwright/.cache/
**/.claude/settings.local.json
+24
View File
@@ -0,0 +1,24 @@
{
"extends": "@istanbuljs/nyc-config-typescript",
"instrument": true,
"sourceMap": true,
"cache": false,
"all": true,
"include": [
"platform/*/src/**/*.ts",
"platform/*/src/**/*.js",
"extensions/*/src/**/*.ts",
"extensions/*/src/**/*.js",
"modes/*/src/**/*.ts",
"modes/*/src/**/*.js"
],
"exclude": [
"**/*.spec.ts",
"**/*.test.ts",
"**/test/**",
"**/tests/**",
"**/examples/**",
"**/stories/**",
"platform/docs/**"
]
}
+3 -1
View File
@@ -109,6 +109,8 @@
"wado",
"wadors",
"wadouri",
"workerpool"
"workerpool",
"Colorbar",
"Colorbars"
]
}
+25 -8
View File
@@ -26,6 +26,7 @@ const ReactRefreshWebpackPlugin = require('@pmmmwh/react-refresh-webpack-plugin'
const NODE_ENV = process.env.NODE_ENV;
const QUICK_BUILD = process.env.QUICK_BUILD;
const BUILD_NUM = process.env.CIRCLE_BUILD_NUM || '0';
const IS_COVERAGE = process.env.COVERAGE === 'true';
// read from ../version.txt
const VERSION_NUMBER = fs.readFileSync(path.join(__dirname, '../version.txt'), 'utf8') || '';
@@ -100,14 +101,30 @@ module.exports = (env, argv, { SRC_DIR, ENTRY }) => {
...(isProdBuild
? []
: [
{
test: /\.[jt]sx?$/,
exclude: /node_modules/,
loader: 'babel-loader',
options: {
plugins: isProdBuild ? [] : ['react-refresh/babel'],
},
},
...(IS_COVERAGE
? [
{
test: /\.[jt]sx?$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-typescript', '@babel/preset-react'],
plugins: ['istanbul'],
},
},
},
]
: [
{
test: /\.[jt]sx?$/,
exclude: /node_modules/,
loader: 'babel-loader',
options: {
plugins: isProdBuild ? [] : ['react-refresh/babel'],
},
},
]),
]),
{
test: /\.svg?$/,
+248
View File
@@ -3,6 +3,254 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [3.11.0-beta.26](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.25...v3.11.0-beta.26) (2025-05-07)
### Features
* add locale files for Russian language, translated texts in locale json files to Russian language. ([#4984](https://github.com/OHIF/Viewers/issues/4984)) ([c829270](https://github.com/OHIF/Viewers/commit/c829270c80c0899132aff0294b8e7386475dc71c))
# [3.11.0-beta.25](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.24...v3.11.0-beta.25) (2025-05-06)
### Features
* **i18n:** add Arabic translations ([#5013](https://github.com/OHIF/Viewers/issues/5013)) ([8218a9d](https://github.com/OHIF/Viewers/commit/8218a9d0346fe9a5473420e26a0b8ab389f4ef58))
# [3.11.0-beta.24](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.23...v3.11.0-beta.24) (2025-05-05)
### Features
* **data overlay:** add foreground background control plus orientation change in the UI and add RTDOSE support ([#5007](https://github.com/OHIF/Viewers/issues/5007)) ([cd4e640](https://github.com/OHIF/Viewers/commit/cd4e6408bc2964e5ee27427cdc328751eaf4d088))
# [3.11.0-beta.23](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.22...v3.11.0-beta.23) (2025-05-05)
**Note:** Version bump only for package ohif-monorepo-root
# [3.11.0-beta.22](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.21...v3.11.0-beta.22) (2025-05-02)
### Features
* improve menu modals customizations ([#5011](https://github.com/OHIF/Viewers/issues/5011)) ([7dbe5e3](https://github.com/OHIF/Viewers/commit/7dbe5e37b06056fdd5262a131417013d112c3d30))
# [3.11.0-beta.21](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.20...v3.11.0-beta.21) (2025-05-02)
**Note:** Version bump only for package ohif-monorepo-root
# [3.11.0-beta.20](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.19...v3.11.0-beta.20) (2025-05-01)
**Note:** Version bump only for package ohif-monorepo-root
# [3.11.0-beta.19](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.18...v3.11.0-beta.19) (2025-05-01)
**Note:** Version bump only for package ohif-monorepo-root
# [3.11.0-beta.18](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.17...v3.11.0-beta.18) (2025-04-30)
**Note:** Version bump only for package ohif-monorepo-root
# [3.11.0-beta.17](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.16...v3.11.0-beta.17) (2025-04-29)
### Bug Fixes
* **referencelines:** and bring back the notification for worker updates but disallow duplication ([#5005](https://github.com/OHIF/Viewers/issues/5005)) ([98f8187](https://github.com/OHIF/Viewers/commit/98f8187dfeb8aec6eca145da75c855aec0da4e1d))
# [3.11.0-beta.16](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.15...v3.11.0-beta.16) (2025-04-29)
### Bug Fixes
* **template:** Update mode template to match current implementation (OHI-1833) ([#4998](https://github.com/OHIF/Viewers/issues/4998)) ([0f39496](https://github.com/OHIF/Viewers/commit/0f394962904808a140221dbae063ca5dcf0df36b))
# [3.11.0-beta.15](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.14...v3.11.0-beta.15) (2025-04-22)
### Bug Fixes
* **docs:** Fix SSR build errors by dynamically importing UI components and resolving other issues ([#4991](https://github.com/OHIF/Viewers/issues/4991)) ([09ac43f](https://github.com/OHIF/Viewers/commit/09ac43f297fa81101d3a3eaa9b5cfef8e01ea214))
# [3.11.0-beta.14](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.13...v3.11.0-beta.14) (2025-04-22)
**Note:** Version bump only for package ohif-monorepo-root
# [3.11.0-beta.13](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.12...v3.11.0-beta.13) (2025-04-22)
**Note:** Version bump only for package ohif-monorepo-root
# [3.11.0-beta.12](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.11...v3.11.0-beta.12) (2025-04-22)
### Features
* Improve unsupported displayset messages ([#4979](https://github.com/OHIF/Viewers/issues/4979)) ([3b8faa8](https://github.com/OHIF/Viewers/commit/3b8faa8a76dde5088815431a947fec07db4a3c7f))
# [3.11.0-beta.11](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.10...v3.11.0-beta.11) (2025-04-18)
### Bug Fixes
* **dicomWebClient:** use public getter for activeDataSourceName instead of accessing private property ([#4973](https://github.com/OHIF/Viewers/issues/4973)) ([5688a2b](https://github.com/OHIF/Viewers/commit/5688a2b4b8f872163ff6492bb80d12cade4385cd))
# [3.11.0-beta.10](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.9...v3.11.0-beta.10) (2025-04-17)
**Note:** Version bump only for package ohif-monorepo-root
# [3.11.0-beta.9](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.8...v3.11.0-beta.9) (2025-04-17)
### Bug Fixes
* **ExtensionManager:** getDataSources method ([#4959](https://github.com/OHIF/Viewers/issues/4959)) ([66a16cd](https://github.com/OHIF/Viewers/commit/66a16cd22f6cedc6e4287dd1304e1f87de4c0d08))
# [3.11.0-beta.8](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.7...v3.11.0-beta.8) (2025-04-16)
**Note:** Version bump only for package ohif-monorepo-root
# [3.11.0-beta.7](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.6...v3.11.0-beta.7) (2025-04-16)
### Bug Fixes
* github runner ([#4961](https://github.com/OHIF/Viewers/issues/4961)) ([9535422](https://github.com/OHIF/Viewers/commit/953542236116aea6f4dce6275b8cdbd4490f4520))
# [3.11.0-beta.6](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.5...v3.11.0-beta.6) (2025-04-16)
**Note:** Version bump only for package ohif-monorepo-root
# [3.11.0-beta.5](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.4...v3.11.0-beta.5) (2025-04-14)
### Bug Fixes
* making onnxruntime work with rsbuild ([#4957](https://github.com/OHIF/Viewers/issues/4957)) ([4a87302](https://github.com/OHIF/Viewers/commit/4a87302f06378649680370f10dd942d31c1c61fb))
# [3.11.0-beta.4](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.3...v3.11.0-beta.4) (2025-04-11)
**Note:** Version bump only for package ohif-monorepo-root
# [3.11.0-beta.3](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.2...v3.11.0-beta.3) (2025-04-11)
### Bug Fixes
* add back study browser scrollbar ([#4942](https://github.com/OHIF/Viewers/issues/4942)) ([ed94df0](https://github.com/OHIF/Viewers/commit/ed94df0dfe007748e51de959d9dd4101485d8df4))
* enable measurements panel scroll ([#4943](https://github.com/OHIF/Viewers/issues/4943)) ([7a36eba](https://github.com/OHIF/Viewers/commit/7a36eba67c6365f3f8cabb53a31ae1eee96069e8))
### Features
* expose scroll thumbnail into view ([#4941](https://github.com/OHIF/Viewers/issues/4941)) ([4926a95](https://github.com/OHIF/Viewers/commit/4926a9532c89011ef932fad1dbae643806e780a8))
# [3.11.0-beta.2](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.1...v3.11.0-beta.2) (2025-04-11)
### Bug Fixes
* make datasource selection work ([#4944](https://github.com/OHIF/Viewers/issues/4944)) ([a9b180c](https://github.com/OHIF/Viewers/commit/a9b180c5d427ec5a6823991cf0c6604be7af1cc2))
# [3.11.0-beta.1](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.0...v3.11.0-beta.1) (2025-04-10)
**Note:** Version bump only for package ohif-monorepo-root
+200
View File
@@ -3,6 +3,206 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [3.11.0-beta.26](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.25...v3.11.0-beta.26) (2025-05-07)
**Note:** Version bump only for package @externals/devDependencies
# [3.11.0-beta.25](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.24...v3.11.0-beta.25) (2025-05-06)
**Note:** Version bump only for package @externals/devDependencies
# [3.11.0-beta.24](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.23...v3.11.0-beta.24) (2025-05-05)
**Note:** Version bump only for package @externals/devDependencies
# [3.11.0-beta.23](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.22...v3.11.0-beta.23) (2025-05-05)
**Note:** Version bump only for package @externals/devDependencies
# [3.11.0-beta.22](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.21...v3.11.0-beta.22) (2025-05-02)
**Note:** Version bump only for package @externals/devDependencies
# [3.11.0-beta.21](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.20...v3.11.0-beta.21) (2025-05-02)
**Note:** Version bump only for package @externals/devDependencies
# [3.11.0-beta.20](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.19...v3.11.0-beta.20) (2025-05-01)
**Note:** Version bump only for package @externals/devDependencies
# [3.11.0-beta.19](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.18...v3.11.0-beta.19) (2025-05-01)
**Note:** Version bump only for package @externals/devDependencies
# [3.11.0-beta.18](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.17...v3.11.0-beta.18) (2025-04-30)
**Note:** Version bump only for package @externals/devDependencies
# [3.11.0-beta.17](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.16...v3.11.0-beta.17) (2025-04-29)
**Note:** Version bump only for package @externals/devDependencies
# [3.11.0-beta.16](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.15...v3.11.0-beta.16) (2025-04-29)
**Note:** Version bump only for package @externals/devDependencies
# [3.11.0-beta.15](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.14...v3.11.0-beta.15) (2025-04-22)
**Note:** Version bump only for package @externals/devDependencies
# [3.11.0-beta.14](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.13...v3.11.0-beta.14) (2025-04-22)
**Note:** Version bump only for package @externals/devDependencies
# [3.11.0-beta.13](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.12...v3.11.0-beta.13) (2025-04-22)
**Note:** Version bump only for package @externals/devDependencies
# [3.11.0-beta.12](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.11...v3.11.0-beta.12) (2025-04-22)
**Note:** Version bump only for package @externals/devDependencies
# [3.11.0-beta.11](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.10...v3.11.0-beta.11) (2025-04-18)
**Note:** Version bump only for package @externals/devDependencies
# [3.11.0-beta.10](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.9...v3.11.0-beta.10) (2025-04-17)
**Note:** Version bump only for package @externals/devDependencies
# [3.11.0-beta.9](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.8...v3.11.0-beta.9) (2025-04-17)
**Note:** Version bump only for package @externals/devDependencies
# [3.11.0-beta.8](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.7...v3.11.0-beta.8) (2025-04-16)
**Note:** Version bump only for package @externals/devDependencies
# [3.11.0-beta.7](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.6...v3.11.0-beta.7) (2025-04-16)
**Note:** Version bump only for package @externals/devDependencies
# [3.11.0-beta.6](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.5...v3.11.0-beta.6) (2025-04-16)
**Note:** Version bump only for package @externals/devDependencies
# [3.11.0-beta.5](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.4...v3.11.0-beta.5) (2025-04-14)
**Note:** Version bump only for package @externals/devDependencies
# [3.11.0-beta.4](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.3...v3.11.0-beta.4) (2025-04-11)
**Note:** Version bump only for package @externals/devDependencies
# [3.11.0-beta.3](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.2...v3.11.0-beta.3) (2025-04-11)
**Note:** Version bump only for package @externals/devDependencies
# [3.11.0-beta.2](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.1...v3.11.0-beta.2) (2025-04-11)
**Note:** Version bump only for package @externals/devDependencies
# [3.11.0-beta.1](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.0...v3.11.0-beta.1) (2025-04-10)
**Note:** Version bump only for package @externals/devDependencies
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "@externals/devDependencies",
"description": "External dev dependencies - put dev build dependencies here",
"version": "3.11.0-beta.1",
"version": "3.11.0-beta.26",
"license": "MIT",
"private": true,
"engines": {
+200
View File
@@ -3,6 +3,206 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [3.11.0-beta.26](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.25...v3.11.0-beta.26) (2025-05-07)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.11.0-beta.25](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.24...v3.11.0-beta.25) (2025-05-06)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.11.0-beta.24](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.23...v3.11.0-beta.24) (2025-05-05)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.11.0-beta.23](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.22...v3.11.0-beta.23) (2025-05-05)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.11.0-beta.22](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.21...v3.11.0-beta.22) (2025-05-02)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.11.0-beta.21](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.20...v3.11.0-beta.21) (2025-05-02)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.11.0-beta.20](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.19...v3.11.0-beta.20) (2025-05-01)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.11.0-beta.19](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.18...v3.11.0-beta.19) (2025-05-01)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.11.0-beta.18](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.17...v3.11.0-beta.18) (2025-04-30)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.11.0-beta.17](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.16...v3.11.0-beta.17) (2025-04-29)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.11.0-beta.16](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.15...v3.11.0-beta.16) (2025-04-29)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.11.0-beta.15](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.14...v3.11.0-beta.15) (2025-04-22)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.11.0-beta.14](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.13...v3.11.0-beta.14) (2025-04-22)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.11.0-beta.13](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.12...v3.11.0-beta.13) (2025-04-22)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.11.0-beta.12](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.11...v3.11.0-beta.12) (2025-04-22)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.11.0-beta.11](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.10...v3.11.0-beta.11) (2025-04-18)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.11.0-beta.10](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.9...v3.11.0-beta.10) (2025-04-17)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.11.0-beta.9](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.8...v3.11.0-beta.9) (2025-04-17)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.11.0-beta.8](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.7...v3.11.0-beta.8) (2025-04-16)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.11.0-beta.7](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.6...v3.11.0-beta.7) (2025-04-16)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.11.0-beta.6](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.5...v3.11.0-beta.6) (2025-04-16)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.11.0-beta.5](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.4...v3.11.0-beta.5) (2025-04-14)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.11.0-beta.4](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.3...v3.11.0-beta.4) (2025-04-11)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.11.0-beta.3](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.2...v3.11.0-beta.3) (2025-04-11)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.11.0-beta.2](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.1...v3.11.0-beta.2) (2025-04-11)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
# [3.11.0-beta.1](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.0...v3.11.0-beta.1) (2025-04-10)
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "@externals/dicom-microscopy-viewer",
"description": "External reference to dicom-microscopy-viewer",
"version": "3.11.0-beta.1",
"version": "3.11.0-beta.26",
"license": "MIT",
"dependencies": {
"dicom-microscopy-viewer": "^0.46.1"
+1
View File
@@ -24,6 +24,7 @@ module.exports = {
'@babel/preset-typescript',
],
plugins: [
'babel-plugin-istanbul',
'@babel/plugin-proposal-object-rest-spread',
'@babel/plugin-syntax-dynamic-import',
'@babel/plugin-transform-regenerator',
+515 -308
View File
File diff suppressed because it is too large. Load diff
+1 -1
View File
@@ -1 +1 @@
ce0d152f92328998eb4f8994278663c720ee5dc0
c829270c80c0899132aff0294b8e7386475dc71c
@@ -3,6 +3,209 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [3.11.0-beta.26](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.25...v3.11.0-beta.26) (2025-05-07)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-pmap
# [3.11.0-beta.25](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.24...v3.11.0-beta.25) (2025-05-06)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-pmap
# [3.11.0-beta.24](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.23...v3.11.0-beta.24) (2025-05-05)
### Features
* **data overlay:** add foreground background control plus orientation change in the UI and add RTDOSE support ([#5007](https://github.com/OHIF/Viewers/issues/5007)) ([cd4e640](https://github.com/OHIF/Viewers/commit/cd4e6408bc2964e5ee27427cdc328751eaf4d088))
# [3.11.0-beta.23](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.22...v3.11.0-beta.23) (2025-05-05)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-pmap
# [3.11.0-beta.22](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.21...v3.11.0-beta.22) (2025-05-02)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-pmap
# [3.11.0-beta.21](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.20...v3.11.0-beta.21) (2025-05-02)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-pmap
# [3.11.0-beta.20](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.19...v3.11.0-beta.20) (2025-05-01)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-pmap
# [3.11.0-beta.19](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.18...v3.11.0-beta.19) (2025-05-01)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-pmap
# [3.11.0-beta.18](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.17...v3.11.0-beta.18) (2025-04-30)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-pmap
# [3.11.0-beta.17](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.16...v3.11.0-beta.17) (2025-04-29)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-pmap
# [3.11.0-beta.16](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.15...v3.11.0-beta.16) (2025-04-29)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-pmap
# [3.11.0-beta.15](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.14...v3.11.0-beta.15) (2025-04-22)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-pmap
# [3.11.0-beta.14](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.13...v3.11.0-beta.14) (2025-04-22)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-pmap
# [3.11.0-beta.13](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.12...v3.11.0-beta.13) (2025-04-22)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-pmap
# [3.11.0-beta.12](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.11...v3.11.0-beta.12) (2025-04-22)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-pmap
# [3.11.0-beta.11](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.10...v3.11.0-beta.11) (2025-04-18)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-pmap
# [3.11.0-beta.10](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.9...v3.11.0-beta.10) (2025-04-17)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-pmap
# [3.11.0-beta.9](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.8...v3.11.0-beta.9) (2025-04-17)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-pmap
# [3.11.0-beta.8](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.7...v3.11.0-beta.8) (2025-04-16)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-pmap
# [3.11.0-beta.7](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.6...v3.11.0-beta.7) (2025-04-16)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-pmap
# [3.11.0-beta.6](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.5...v3.11.0-beta.6) (2025-04-16)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-pmap
# [3.11.0-beta.5](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.4...v3.11.0-beta.5) (2025-04-14)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-pmap
# [3.11.0-beta.4](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.3...v3.11.0-beta.4) (2025-04-11)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-pmap
# [3.11.0-beta.3](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.2...v3.11.0-beta.3) (2025-04-11)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-pmap
# [3.11.0-beta.2](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.1...v3.11.0-beta.2) (2025-04-11)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-pmap
# [3.11.0-beta.1](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.0...v3.11.0-beta.1) (2025-04-10)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-pmap
@@ -1,6 +1,6 @@
{
"name": "@ohif/extension-cornerstone-dicom-pmap",
"version": "3.11.0-beta.1",
"version": "3.11.0-beta.26",
"description": "DICOM Parametric Map read workflow",
"author": "OHIF",
"license": "MIT",
@@ -33,10 +33,10 @@
"start": "yarn run dev"
},
"peerDependencies": {
"@ohif/core": "3.11.0-beta.1",
"@ohif/extension-cornerstone": "3.11.0-beta.1",
"@ohif/extension-default": "3.11.0-beta.1",
"@ohif/i18n": "3.11.0-beta.1",
"@ohif/core": "3.11.0-beta.26",
"@ohif/extension-cornerstone": "3.11.0-beta.26",
"@ohif/extension-default": "3.11.0-beta.26",
"@ohif/i18n": "3.11.0-beta.26",
"prop-types": "^15.6.2",
"react": "^18.3.1",
"react-dom": "^18.3.1",
@@ -46,8 +46,8 @@
},
"dependencies": {
"@babel/runtime": "^7.20.13",
"@cornerstonejs/adapters": "^3.9.5",
"@cornerstonejs/core": "^3.9.5",
"@cornerstonejs/adapters": "^3.11.6",
"@cornerstonejs/core": "^3.11.6",
"@kitware/vtk.js": "32.12.0",
"react-color": "^2.19.3"
}
@@ -1,10 +1,9 @@
import { utils } from '@ohif/core';
import { utils, Types as OhifTypes } from '@ohif/core';
import i18n from '@ohif/i18n';
import { metaData, cache, utilities as csUtils, volumeLoader } from '@cornerstonejs/core';
import { adaptersPMAP } from '@cornerstonejs/adapters';
import { SOPClassHandlerId } from './id';
import { dicomLoaderService } from '@ohif/extension-cornerstone';
const VOLUME_LOADER_SCHEME = 'cornerstoneStreamingImageVolume';
import { dicomLoaderService, VOLUME_LOADER_SCHEME } from '@ohif/extension-cornerstone';
const sopClassUids = ['1.2.840.10008.5.1.4.1.1.30'];
function _getDisplaySetsFromSeries(
@@ -57,7 +56,8 @@ function _getDisplaySetsFromSeries(
wadoRoot,
wadoUriRoot,
wadoUri,
isOverlayDisplaySet: true,
supportsWindowLevel: true,
label: SeriesDescription || `${i18n.t('Series')} ${SeriesNumber} - ${i18n.t('PMAP')}`,
};
const referencedSeriesSequence = instance.ReferencedSeriesSequence;
@@ -227,7 +227,8 @@ async function _loadParametricMap({ displaySet, headers }: withAppTypes) {
return derivedVolume;
}
function getSopClassHandlerModule({ servicesManager, extensionManager }) {
function getSopClassHandlerModule(params: OhifTypes.Extensions.ExtensionParams) {
const { servicesManager, extensionManager } = params;
const getDisplaySetsFromSeries = instances => {
return _getDisplaySetsFromSeries(instances, servicesManager, extensionManager);
};
@@ -3,6 +3,209 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [3.11.0-beta.26](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.25...v3.11.0-beta.26) (2025-05-07)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
# [3.11.0-beta.25](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.24...v3.11.0-beta.25) (2025-05-06)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
# [3.11.0-beta.24](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.23...v3.11.0-beta.24) (2025-05-05)
### Features
* **data overlay:** add foreground background control plus orientation change in the UI and add RTDOSE support ([#5007](https://github.com/OHIF/Viewers/issues/5007)) ([cd4e640](https://github.com/OHIF/Viewers/commit/cd4e6408bc2964e5ee27427cdc328751eaf4d088))
# [3.11.0-beta.23](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.22...v3.11.0-beta.23) (2025-05-05)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
# [3.11.0-beta.22](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.21...v3.11.0-beta.22) (2025-05-02)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
# [3.11.0-beta.21](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.20...v3.11.0-beta.21) (2025-05-02)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
# [3.11.0-beta.20](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.19...v3.11.0-beta.20) (2025-05-01)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
# [3.11.0-beta.19](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.18...v3.11.0-beta.19) (2025-05-01)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
# [3.11.0-beta.18](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.17...v3.11.0-beta.18) (2025-04-30)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
# [3.11.0-beta.17](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.16...v3.11.0-beta.17) (2025-04-29)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
# [3.11.0-beta.16](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.15...v3.11.0-beta.16) (2025-04-29)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
# [3.11.0-beta.15](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.14...v3.11.0-beta.15) (2025-04-22)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
# [3.11.0-beta.14](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.13...v3.11.0-beta.14) (2025-04-22)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
# [3.11.0-beta.13](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.12...v3.11.0-beta.13) (2025-04-22)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
# [3.11.0-beta.12](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.11...v3.11.0-beta.12) (2025-04-22)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
# [3.11.0-beta.11](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.10...v3.11.0-beta.11) (2025-04-18)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
# [3.11.0-beta.10](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.9...v3.11.0-beta.10) (2025-04-17)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
# [3.11.0-beta.9](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.8...v3.11.0-beta.9) (2025-04-17)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
# [3.11.0-beta.8](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.7...v3.11.0-beta.8) (2025-04-16)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
# [3.11.0-beta.7](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.6...v3.11.0-beta.7) (2025-04-16)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
# [3.11.0-beta.6](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.5...v3.11.0-beta.6) (2025-04-16)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
# [3.11.0-beta.5](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.4...v3.11.0-beta.5) (2025-04-14)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
# [3.11.0-beta.4](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.3...v3.11.0-beta.4) (2025-04-11)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
# [3.11.0-beta.3](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.2...v3.11.0-beta.3) (2025-04-11)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
# [3.11.0-beta.2](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.1...v3.11.0-beta.2) (2025-04-11)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
# [3.11.0-beta.1](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.0...v3.11.0-beta.1) (2025-04-10)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
+5 -5
View File
@@ -1,6 +1,6 @@
{
"name": "@ohif/extension-cornerstone-dicom-rt",
"version": "3.11.0-beta.1",
"version": "3.11.0-beta.26",
"description": "DICOM RT read workflow",
"author": "OHIF",
"license": "MIT",
@@ -33,10 +33,10 @@
"start": "yarn run dev"
},
"peerDependencies": {
"@ohif/core": "3.11.0-beta.1",
"@ohif/extension-cornerstone": "3.11.0-beta.1",
"@ohif/extension-default": "3.11.0-beta.1",
"@ohif/i18n": "3.11.0-beta.1",
"@ohif/core": "3.11.0-beta.26",
"@ohif/extension-cornerstone": "3.11.0-beta.26",
"@ohif/extension-default": "3.11.0-beta.26",
"@ohif/i18n": "3.11.0-beta.26",
"prop-types": "^15.6.2",
"react": "^18.3.1",
"react-dom": "^18.3.1",
@@ -27,7 +27,7 @@ const commandsModule = ({ commandsManager, servicesManager }: withAppTypes) => {
// it will be in the correct position zoom and pan
commandsManager.runCommand('updateStoredPositionPresentation', {
viewportId,
displaySetInstanceUID: referencedDisplaySet.displaySetInstanceUID,
displaySetInstanceUIDs: [referencedDisplaySet.displaySetInstanceUID],
});
viewportGridService.setDisplaySetsForViewport({
@@ -1,4 +1,5 @@
import { utils } from '@ohif/core';
import { utils, Types as OhifTypes } from '@ohif/core';
import i18n from '@ohif/i18n';
import { SOPClassHandlerId } from './id';
import loadRTStruct from './loadRTStruct';
@@ -53,6 +54,7 @@ function _getDisplaySetsFromSeries(
wadoUriRoot,
wadoUri,
isOverlayDisplaySet: true,
label: SeriesDescription || `${i18n.t('Series')} ${SeriesNumber} - ${i18n.t('RTSTRUCT')}`,
};
let referencedSeriesSequence = instance.ReferencedSeriesSequence;
@@ -94,12 +96,19 @@ function _getDisplaySetsFromSeries(
displaySet.referencedDisplaySetInstanceUID = referencedDisplaySet.displaySetInstanceUID;
}
displaySet.load = ({ headers }) => _load(displaySet, servicesManager, extensionManager, headers);
displaySet.load = ({ headers, createSegmentation = true }) =>
_load(displaySet, servicesManager, extensionManager, headers, createSegmentation);
return [displaySet];
}
function _load(rtDisplaySet, servicesManager: AppTypes.ServicesManager, extensionManager, headers) {
function _load(
rtDisplaySet,
servicesManager: AppTypes.ServicesManager,
extensionManager,
headers,
createSegmentation = true
) {
const { SOPInstanceUID } = rtDisplaySet;
const { segmentationService } = servicesManager.services;
if (
@@ -121,16 +130,21 @@ function _load(rtDisplaySet, servicesManager: AppTypes.ServicesManager, extensio
rtDisplaySet.structureSet = structureSet;
}
segmentationService
.createSegmentationForRTDisplaySet(rtDisplaySet)
.then(() => {
rtDisplaySet.loading = false;
resolve();
})
.catch(error => {
rtDisplaySet.loading = false;
reject(error);
});
if (createSegmentation) {
segmentationService
.createSegmentationForRTDisplaySet(rtDisplaySet)
.then(() => {
rtDisplaySet.loading = false;
resolve();
})
.catch(error => {
rtDisplaySet.loading = false;
reject(error);
});
} else {
rtDisplaySet.loading = false;
resolve();
}
});
return loadPromises[SOPInstanceUID];
@@ -171,20 +185,14 @@ function _deriveReferencedSeriesSequenceFromFrameOfReferenceSequence(
return ReferencedSeriesSequence;
}
function _segmentationExistsInCache(
rtDisplaySet,
segmentationService: AppTypes.SegmentationService
) {
function _segmentationExistsInCache() {
// Todo: fix this
return false;
// This should be abstracted with the CornerstoneCacheService
const rtContourId = rtDisplaySet.displaySetInstanceUID;
const contour = segmentationService.getContour(rtContourId);
return contour !== undefined;
}
function getSopClassHandlerModule({ servicesManager, extensionManager }) {
function getSopClassHandlerModule(params: OhifTypes.Extensions.ExtensionParams) {
const { servicesManager, extensionManager } = params;
return [
{
name: 'dicom-rt',
@@ -323,7 +323,7 @@ function OHIFCornerstoneRTViewport(props: withAppTypes) {
onStatusClick,
}),
indexPriority: -100,
location: viewportActionCornersService.LOCATIONS.topLeft,
location: viewportActionCornersService.LOCATIONS.topRight,
},
{
viewportId,
@@ -3,6 +3,209 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [3.11.0-beta.26](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.25...v3.11.0-beta.26) (2025-05-07)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-seg
# [3.11.0-beta.25](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.24...v3.11.0-beta.25) (2025-05-06)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-seg
# [3.11.0-beta.24](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.23...v3.11.0-beta.24) (2025-05-05)
### Features
* **data overlay:** add foreground background control plus orientation change in the UI and add RTDOSE support ([#5007](https://github.com/OHIF/Viewers/issues/5007)) ([cd4e640](https://github.com/OHIF/Viewers/commit/cd4e6408bc2964e5ee27427cdc328751eaf4d088))
# [3.11.0-beta.23](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.22...v3.11.0-beta.23) (2025-05-05)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-seg
# [3.11.0-beta.22](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.21...v3.11.0-beta.22) (2025-05-02)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-seg
# [3.11.0-beta.21](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.20...v3.11.0-beta.21) (2025-05-02)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-seg
# [3.11.0-beta.20](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.19...v3.11.0-beta.20) (2025-05-01)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-seg
# [3.11.0-beta.19](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.18...v3.11.0-beta.19) (2025-05-01)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-seg
# [3.11.0-beta.18](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.17...v3.11.0-beta.18) (2025-04-30)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-seg
# [3.11.0-beta.17](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.16...v3.11.0-beta.17) (2025-04-29)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-seg
# [3.11.0-beta.16](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.15...v3.11.0-beta.16) (2025-04-29)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-seg
# [3.11.0-beta.15](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.14...v3.11.0-beta.15) (2025-04-22)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-seg
# [3.11.0-beta.14](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.13...v3.11.0-beta.14) (2025-04-22)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-seg
# [3.11.0-beta.13](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.12...v3.11.0-beta.13) (2025-04-22)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-seg
# [3.11.0-beta.12](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.11...v3.11.0-beta.12) (2025-04-22)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-seg
# [3.11.0-beta.11](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.10...v3.11.0-beta.11) (2025-04-18)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-seg
# [3.11.0-beta.10](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.9...v3.11.0-beta.10) (2025-04-17)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-seg
# [3.11.0-beta.9](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.8...v3.11.0-beta.9) (2025-04-17)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-seg
# [3.11.0-beta.8](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.7...v3.11.0-beta.8) (2025-04-16)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-seg
# [3.11.0-beta.7](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.6...v3.11.0-beta.7) (2025-04-16)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-seg
# [3.11.0-beta.6](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.5...v3.11.0-beta.6) (2025-04-16)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-seg
# [3.11.0-beta.5](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.4...v3.11.0-beta.5) (2025-04-14)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-seg
# [3.11.0-beta.4](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.3...v3.11.0-beta.4) (2025-04-11)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-seg
# [3.11.0-beta.3](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.2...v3.11.0-beta.3) (2025-04-11)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-seg
# [3.11.0-beta.2](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.1...v3.11.0-beta.2) (2025-04-11)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-seg
# [3.11.0-beta.1](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.0...v3.11.0-beta.1) (2025-04-10)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-seg
@@ -1,6 +1,6 @@
{
"name": "@ohif/extension-cornerstone-dicom-seg",
"version": "3.11.0-beta.1",
"version": "3.11.0-beta.26",
"description": "DICOM SEG read workflow",
"author": "OHIF",
"license": "MIT",
@@ -33,10 +33,10 @@
"start": "yarn run dev"
},
"peerDependencies": {
"@ohif/core": "3.11.0-beta.1",
"@ohif/extension-cornerstone": "3.11.0-beta.1",
"@ohif/extension-default": "3.11.0-beta.1",
"@ohif/i18n": "3.11.0-beta.1",
"@ohif/core": "3.11.0-beta.26",
"@ohif/extension-cornerstone": "3.11.0-beta.26",
"@ohif/extension-default": "3.11.0-beta.26",
"@ohif/i18n": "3.11.0-beta.26",
"prop-types": "^15.6.2",
"react": "^18.3.1",
"react-dom": "^18.3.1",
@@ -46,8 +46,8 @@
},
"dependencies": {
"@babel/runtime": "^7.20.13",
"@cornerstonejs/adapters": "^3.9.5",
"@cornerstonejs/core": "^3.9.5",
"@cornerstonejs/adapters": "^3.11.6",
"@cornerstonejs/core": "^3.11.6",
"@kitware/vtk.js": "32.12.0",
"react-color": "^2.19.3"
}
@@ -1,5 +1,6 @@
import { utils } from '@ohif/core';
import { metaData, triggerEvent, eventTarget } from '@cornerstonejs/core';
import { utils, Types as OhifTypes } from '@ohif/core';
import i18n from '@ohif/i18n';
import { metaData, eventTarget } from '@cornerstonejs/core';
import { CONSTANTS, segmentation as cstSegmentation } from '@cornerstonejs/tools';
import { adaptersSEG, Enums } from '@cornerstonejs/adapters';
@@ -57,6 +58,7 @@ function _getDisplaySetsFromSeries(
wadoUriRoot,
wadoUri,
isOverlayDisplaySet: true,
label: SeriesDescription || `${i18n.t('Series')} ${SeriesNumber} - ${i18n.t('SEG')}`,
};
const referencedSeriesSequence = instance.ReferencedSeriesSequence;
@@ -232,7 +234,8 @@ function _segmentationExists(segDisplaySet) {
return cstSegmentation.state.getSegmentation(segDisplaySet.displaySetInstanceUID);
}
function getSopClassHandlerModule({ servicesManager, extensionManager }) {
function getSopClassHandlerModule(params: OhifTypes.Extensions.ExtensionParams) {
const { servicesManager, extensionManager } = params;
const getDisplaySetsFromSeries = instances => {
return _getDisplaySetsFromSeries(instances, servicesManager, extensionManager);
};
@@ -157,7 +157,7 @@ function OHIFCornerstoneSEGViewport(props: withAppTypes) {
// it will be in the correct position zoom and pan
commandsManager.runCommand('updateStoredPositionPresentation', {
viewportId,
displaySetInstanceUID: referencedDisplaySet.displaySetInstanceUID,
displaySetInstanceUIDs: [referencedDisplaySet.displaySetInstanceUID],
});
commandsManager.runCommand('loadSegmentationDisplaySetsForViewport', {
@@ -304,7 +304,7 @@ function OHIFCornerstoneSEGViewport(props: withAppTypes) {
onStatusClick,
}),
indexPriority: -100,
location: viewportActionCornersService.LOCATIONS.topLeft,
location: viewportActionCornersService.LOCATIONS.topRight,
},
{
viewportId,
@@ -3,6 +3,209 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [3.11.0-beta.26](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.25...v3.11.0-beta.26) (2025-05-07)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-sr
# [3.11.0-beta.25](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.24...v3.11.0-beta.25) (2025-05-06)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-sr
# [3.11.0-beta.24](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.23...v3.11.0-beta.24) (2025-05-05)
### Features
* **data overlay:** add foreground background control plus orientation change in the UI and add RTDOSE support ([#5007](https://github.com/OHIF/Viewers/issues/5007)) ([cd4e640](https://github.com/OHIF/Viewers/commit/cd4e6408bc2964e5ee27427cdc328751eaf4d088))
# [3.11.0-beta.23](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.22...v3.11.0-beta.23) (2025-05-05)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-sr
# [3.11.0-beta.22](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.21...v3.11.0-beta.22) (2025-05-02)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-sr
# [3.11.0-beta.21](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.20...v3.11.0-beta.21) (2025-05-02)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-sr
# [3.11.0-beta.20](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.19...v3.11.0-beta.20) (2025-05-01)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-sr
# [3.11.0-beta.19](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.18...v3.11.0-beta.19) (2025-05-01)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-sr
# [3.11.0-beta.18](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.17...v3.11.0-beta.18) (2025-04-30)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-sr
# [3.11.0-beta.17](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.16...v3.11.0-beta.17) (2025-04-29)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-sr
# [3.11.0-beta.16](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.15...v3.11.0-beta.16) (2025-04-29)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-sr
# [3.11.0-beta.15](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.14...v3.11.0-beta.15) (2025-04-22)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-sr
# [3.11.0-beta.14](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.13...v3.11.0-beta.14) (2025-04-22)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-sr
# [3.11.0-beta.13](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.12...v3.11.0-beta.13) (2025-04-22)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-sr
# [3.11.0-beta.12](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.11...v3.11.0-beta.12) (2025-04-22)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-sr
# [3.11.0-beta.11](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.10...v3.11.0-beta.11) (2025-04-18)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-sr
# [3.11.0-beta.10](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.9...v3.11.0-beta.10) (2025-04-17)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-sr
# [3.11.0-beta.9](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.8...v3.11.0-beta.9) (2025-04-17)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-sr
# [3.11.0-beta.8](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.7...v3.11.0-beta.8) (2025-04-16)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-sr
# [3.11.0-beta.7](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.6...v3.11.0-beta.7) (2025-04-16)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-sr
# [3.11.0-beta.6](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.5...v3.11.0-beta.6) (2025-04-16)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-sr
# [3.11.0-beta.5](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.4...v3.11.0-beta.5) (2025-04-14)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-sr
# [3.11.0-beta.4](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.3...v3.11.0-beta.4) (2025-04-11)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-sr
# [3.11.0-beta.3](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.2...v3.11.0-beta.3) (2025-04-11)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-sr
# [3.11.0-beta.2](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.1...v3.11.0-beta.2) (2025-04-11)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-sr
# [3.11.0-beta.1](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.0...v3.11.0-beta.1) (2025-04-10)
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-sr
+8 -8
View File
@@ -1,6 +1,6 @@
{
"name": "@ohif/extension-cornerstone-dicom-sr",
"version": "3.11.0-beta.1",
"version": "3.11.0-beta.26",
"description": "OHIF extension for an SR Cornerstone Viewport",
"author": "OHIF",
"license": "MIT",
@@ -34,10 +34,10 @@
"test:unit:ci": "jest --ci --runInBand --collectCoverage --passWithNoTests"
},
"peerDependencies": {
"@ohif/core": "3.11.0-beta.1",
"@ohif/extension-cornerstone": "3.11.0-beta.1",
"@ohif/extension-measurement-tracking": "3.11.0-beta.1",
"@ohif/ui": "3.11.0-beta.1",
"@ohif/core": "3.11.0-beta.26",
"@ohif/extension-cornerstone": "3.11.0-beta.26",
"@ohif/extension-measurement-tracking": "3.11.0-beta.26",
"@ohif/ui": "3.11.0-beta.26",
"dcmjs": "*",
"dicom-parser": "^1.8.9",
"hammerjs": "^2.0.8",
@@ -46,9 +46,9 @@
},
"dependencies": {
"@babel/runtime": "^7.20.13",
"@cornerstonejs/adapters": "^3.9.5",
"@cornerstonejs/core": "^3.9.5",
"@cornerstonejs/tools": "^3.9.5",
"@cornerstonejs/adapters": "^3.11.6",
"@cornerstonejs/core": "^3.11.6",
"@cornerstonejs/tools": "^3.11.6",
"classnames": "^2.3.2"
}
}
@@ -273,7 +273,7 @@ function OHIFCornerstoneSRMeasurementViewport(props: withAppTypes) {
servicesManager,
}),
indexPriority: -100,
location: viewportActionCornersService.LOCATIONS.topLeft,
location: viewportActionCornersService.LOCATIONS.topRight,
},
{
viewportId,
@@ -1,4 +1,5 @@
import { utils, classes, DisplaySetService, Types } from '@ohif/core';
import { utils, classes, DisplaySetService, Types as OhifTypes } from '@ohif/core';
import i18n from '@ohif/i18n';
import { Enums as CSExtensionEnums } from '@ohif/extension-cornerstone';
import { adaptersSR } from '@cornerstonejs/adapters';
@@ -122,6 +123,7 @@ function _getDisplaySetsFromSeries(
sopClassUids,
instance,
addInstances,
label: SeriesDescription || `${i18n.t('Series')} ${SeriesNumber} - ${i18n.t('SR')}`,
};
displaySet.load = () => _load(displaySet, servicesManager, extensionManager);
@@ -354,7 +356,8 @@ function _measurementReferencesSOPInstanceUID(measurement, SOPInstanceUID, frame
* @param {Object} options.extensionManager - The extension manager.
* @returns {Array} An array containing the SOP class handler module.
*/
function getSopClassHandlerModule({ servicesManager, extensionManager }) {
function getSopClassHandlerModule(params: OhifTypes.Extensions.ExtensionParams) {
const { servicesManager, extensionManager } = params;
const getDisplaySetsFromSeries = instances => {
return _getDisplaySetsFromSeries(instances, servicesManager, extensionManager);
};
@@ -3,6 +3,209 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [3.11.0-beta.26](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.25...v3.11.0-beta.26) (2025-05-07)
**Note:** Version bump only for package @ohif/extension-cornerstone-dynamic-volume
# [3.11.0-beta.25](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.24...v3.11.0-beta.25) (2025-05-06)
**Note:** Version bump only for package @ohif/extension-cornerstone-dynamic-volume
# [3.11.0-beta.24](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.23...v3.11.0-beta.24) (2025-05-05)
### Features
* **data overlay:** add foreground background control plus orientation change in the UI and add RTDOSE support ([#5007](https://github.com/OHIF/Viewers/issues/5007)) ([cd4e640](https://github.com/OHIF/Viewers/commit/cd4e6408bc2964e5ee27427cdc328751eaf4d088))
# [3.11.0-beta.23](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.22...v3.11.0-beta.23) (2025-05-05)
**Note:** Version bump only for package @ohif/extension-cornerstone-dynamic-volume
# [3.11.0-beta.22](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.21...v3.11.0-beta.22) (2025-05-02)
**Note:** Version bump only for package @ohif/extension-cornerstone-dynamic-volume
# [3.11.0-beta.21](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.20...v3.11.0-beta.21) (2025-05-02)
**Note:** Version bump only for package @ohif/extension-cornerstone-dynamic-volume
# [3.11.0-beta.20](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.19...v3.11.0-beta.20) (2025-05-01)
**Note:** Version bump only for package @ohif/extension-cornerstone-dynamic-volume
# [3.11.0-beta.19](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.18...v3.11.0-beta.19) (2025-05-01)
**Note:** Version bump only for package @ohif/extension-cornerstone-dynamic-volume
# [3.11.0-beta.18](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.17...v3.11.0-beta.18) (2025-04-30)
**Note:** Version bump only for package @ohif/extension-cornerstone-dynamic-volume
# [3.11.0-beta.17](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.16...v3.11.0-beta.17) (2025-04-29)
**Note:** Version bump only for package @ohif/extension-cornerstone-dynamic-volume
# [3.11.0-beta.16](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.15...v3.11.0-beta.16) (2025-04-29)
**Note:** Version bump only for package @ohif/extension-cornerstone-dynamic-volume
# [3.11.0-beta.15](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.14...v3.11.0-beta.15) (2025-04-22)
**Note:** Version bump only for package @ohif/extension-cornerstone-dynamic-volume
# [3.11.0-beta.14](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.13...v3.11.0-beta.14) (2025-04-22)
**Note:** Version bump only for package @ohif/extension-cornerstone-dynamic-volume
# [3.11.0-beta.13](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.12...v3.11.0-beta.13) (2025-04-22)
**Note:** Version bump only for package @ohif/extension-cornerstone-dynamic-volume
# [3.11.0-beta.12](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.11...v3.11.0-beta.12) (2025-04-22)
**Note:** Version bump only for package @ohif/extension-cornerstone-dynamic-volume
# [3.11.0-beta.11](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.10...v3.11.0-beta.11) (2025-04-18)
**Note:** Version bump only for package @ohif/extension-cornerstone-dynamic-volume
# [3.11.0-beta.10](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.9...v3.11.0-beta.10) (2025-04-17)
**Note:** Version bump only for package @ohif/extension-cornerstone-dynamic-volume
# [3.11.0-beta.9](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.8...v3.11.0-beta.9) (2025-04-17)
**Note:** Version bump only for package @ohif/extension-cornerstone-dynamic-volume
# [3.11.0-beta.8](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.7...v3.11.0-beta.8) (2025-04-16)
**Note:** Version bump only for package @ohif/extension-cornerstone-dynamic-volume
# [3.11.0-beta.7](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.6...v3.11.0-beta.7) (2025-04-16)
**Note:** Version bump only for package @ohif/extension-cornerstone-dynamic-volume
# [3.11.0-beta.6](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.5...v3.11.0-beta.6) (2025-04-16)
**Note:** Version bump only for package @ohif/extension-cornerstone-dynamic-volume
# [3.11.0-beta.5](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.4...v3.11.0-beta.5) (2025-04-14)
**Note:** Version bump only for package @ohif/extension-cornerstone-dynamic-volume
# [3.11.0-beta.4](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.3...v3.11.0-beta.4) (2025-04-11)
**Note:** Version bump only for package @ohif/extension-cornerstone-dynamic-volume
# [3.11.0-beta.3](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.2...v3.11.0-beta.3) (2025-04-11)
**Note:** Version bump only for package @ohif/extension-cornerstone-dynamic-volume
# [3.11.0-beta.2](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.1...v3.11.0-beta.2) (2025-04-11)
**Note:** Version bump only for package @ohif/extension-cornerstone-dynamic-volume
# [3.11.0-beta.1](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.0...v3.11.0-beta.1) (2025-04-10)
**Note:** Version bump only for package @ohif/extension-cornerstone-dynamic-volume
@@ -1,6 +1,6 @@
{
"name": "@ohif/extension-cornerstone-dynamic-volume",
"version": "3.11.0-beta.1",
"version": "3.11.0-beta.26",
"description": "OHIF extension for 4D volumes data",
"author": "OHIF",
"license": "MIT",
@@ -29,11 +29,11 @@
"test:unit:ci": "jest --ci --runInBand --collectCoverage --passWithNoTests"
},
"peerDependencies": {
"@ohif/core": "3.11.0-beta.1",
"@ohif/extension-cornerstone": "3.11.0-beta.1",
"@ohif/extension-default": "3.11.0-beta.1",
"@ohif/i18n": "3.11.0-beta.1",
"@ohif/ui": "3.11.0-beta.1",
"@ohif/core": "3.11.0-beta.26",
"@ohif/extension-cornerstone": "3.11.0-beta.26",
"@ohif/extension-default": "3.11.0-beta.26",
"@ohif/i18n": "3.11.0-beta.26",
"@ohif/ui": "3.11.0-beta.26",
"dcmjs": "*",
"dicom-parser": "^1.8.21",
"hammerjs": "^2.0.8",
@@ -42,8 +42,8 @@
},
"dependencies": {
"@babel/runtime": "^7.20.13",
"@cornerstonejs/core": "^3.9.5",
"@cornerstonejs/tools": "^3.9.5",
"@cornerstonejs/core": "^3.11.6",
"@cornerstonejs/tools": "^3.11.6",
"classnames": "^2.3.2"
}
}
+206
View File
@@ -3,6 +3,212 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [3.11.0-beta.26](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.25...v3.11.0-beta.26) (2025-05-07)
**Note:** Version bump only for package @ohif/extension-cornerstone
# [3.11.0-beta.25](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.24...v3.11.0-beta.25) (2025-05-06)
**Note:** Version bump only for package @ohif/extension-cornerstone
# [3.11.0-beta.24](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.23...v3.11.0-beta.24) (2025-05-05)
### Features
* **data overlay:** add foreground background control plus orientation change in the UI and add RTDOSE support ([#5007](https://github.com/OHIF/Viewers/issues/5007)) ([cd4e640](https://github.com/OHIF/Viewers/commit/cd4e6408bc2964e5ee27427cdc328751eaf4d088))
# [3.11.0-beta.23](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.22...v3.11.0-beta.23) (2025-05-05)
**Note:** Version bump only for package @ohif/extension-cornerstone
# [3.11.0-beta.22](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.21...v3.11.0-beta.22) (2025-05-02)
**Note:** Version bump only for package @ohif/extension-cornerstone
# [3.11.0-beta.21](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.20...v3.11.0-beta.21) (2025-05-02)
**Note:** Version bump only for package @ohif/extension-cornerstone
# [3.11.0-beta.20](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.19...v3.11.0-beta.20) (2025-05-01)
**Note:** Version bump only for package @ohif/extension-cornerstone
# [3.11.0-beta.19](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.18...v3.11.0-beta.19) (2025-05-01)
**Note:** Version bump only for package @ohif/extension-cornerstone
# [3.11.0-beta.18](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.17...v3.11.0-beta.18) (2025-04-30)
**Note:** Version bump only for package @ohif/extension-cornerstone
# [3.11.0-beta.17](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.16...v3.11.0-beta.17) (2025-04-29)
### Bug Fixes
* **referencelines:** and bring back the notification for worker updates but disallow duplication ([#5005](https://github.com/OHIF/Viewers/issues/5005)) ([98f8187](https://github.com/OHIF/Viewers/commit/98f8187dfeb8aec6eca145da75c855aec0da4e1d))
# [3.11.0-beta.16](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.15...v3.11.0-beta.16) (2025-04-29)
**Note:** Version bump only for package @ohif/extension-cornerstone
# [3.11.0-beta.15](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.14...v3.11.0-beta.15) (2025-04-22)
**Note:** Version bump only for package @ohif/extension-cornerstone
# [3.11.0-beta.14](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.13...v3.11.0-beta.14) (2025-04-22)
**Note:** Version bump only for package @ohif/extension-cornerstone
# [3.11.0-beta.13](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.12...v3.11.0-beta.13) (2025-04-22)
**Note:** Version bump only for package @ohif/extension-cornerstone
# [3.11.0-beta.12](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.11...v3.11.0-beta.12) (2025-04-22)
**Note:** Version bump only for package @ohif/extension-cornerstone
# [3.11.0-beta.11](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.10...v3.11.0-beta.11) (2025-04-18)
**Note:** Version bump only for package @ohif/extension-cornerstone
# [3.11.0-beta.10](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.9...v3.11.0-beta.10) (2025-04-17)
**Note:** Version bump only for package @ohif/extension-cornerstone
# [3.11.0-beta.9](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.8...v3.11.0-beta.9) (2025-04-17)
**Note:** Version bump only for package @ohif/extension-cornerstone
# [3.11.0-beta.8](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.7...v3.11.0-beta.8) (2025-04-16)
**Note:** Version bump only for package @ohif/extension-cornerstone
# [3.11.0-beta.7](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.6...v3.11.0-beta.7) (2025-04-16)
**Note:** Version bump only for package @ohif/extension-cornerstone
# [3.11.0-beta.6](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.5...v3.11.0-beta.6) (2025-04-16)
**Note:** Version bump only for package @ohif/extension-cornerstone
# [3.11.0-beta.5](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.4...v3.11.0-beta.5) (2025-04-14)
**Note:** Version bump only for package @ohif/extension-cornerstone
# [3.11.0-beta.4](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.3...v3.11.0-beta.4) (2025-04-11)
**Note:** Version bump only for package @ohif/extension-cornerstone
# [3.11.0-beta.3](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.2...v3.11.0-beta.3) (2025-04-11)
**Note:** Version bump only for package @ohif/extension-cornerstone
# [3.11.0-beta.2](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.1...v3.11.0-beta.2) (2025-04-11)
**Note:** Version bump only for package @ohif/extension-cornerstone
# [3.11.0-beta.1](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.0...v3.11.0-beta.1) (2025-04-10)
**Note:** Version bump only for package @ohif/extension-cornerstone
+10 -10
View File
@@ -1,6 +1,6 @@
{
"name": "@ohif/extension-cornerstone",
"version": "3.11.0-beta.1",
"version": "3.11.0-beta.26",
"description": "OHIF extension for Cornerstone",
"author": "OHIF",
"license": "MIT",
@@ -38,9 +38,9 @@
"@cornerstonejs/codec-libjpeg-turbo-8bit": "^1.2.2",
"@cornerstonejs/codec-openjpeg": "^1.2.4",
"@cornerstonejs/codec-openjph": "^2.4.5",
"@cornerstonejs/dicom-image-loader": "^3.9.5",
"@ohif/core": "3.11.0-beta.1",
"@ohif/ui": "3.11.0-beta.1",
"@cornerstonejs/dicom-image-loader": "^3.11.6",
"@ohif/core": "3.11.0-beta.26",
"@ohif/ui": "3.11.0-beta.26",
"dcmjs": "*",
"dicom-parser": "^1.8.21",
"hammerjs": "^2.0.8",
@@ -51,12 +51,12 @@
},
"dependencies": {
"@babel/runtime": "^7.20.13",
"@cornerstonejs/adapters": "^3.9.5",
"@cornerstonejs/ai": "^3.9.5",
"@cornerstonejs/core": "^3.9.5",
"@cornerstonejs/labelmap-interpolation": "^3.9.5",
"@cornerstonejs/polymorphic-segmentation": "^3.9.5",
"@cornerstonejs/tools": "^3.9.5",
"@cornerstonejs/adapters": "^3.11.6",
"@cornerstonejs/ai": "^3.11.6",
"@cornerstonejs/core": "^3.11.6",
"@cornerstonejs/labelmap-interpolation": "^3.11.6",
"@cornerstonejs/polymorphic-segmentation": "^3.11.6",
"@cornerstonejs/tools": "^3.11.6",
"@itk-wasm/morphological-contour-interpolation": "1.1.0",
"@kitware/vtk.js": "32.12.0",
"html2canvas": "^1.4.1",
@@ -1,9 +1,7 @@
import React, { useEffect, useRef, useCallback, useState } from 'react';
import { useResizeDetector } from 'react-resize-detector';
import * as cs3DTools from '@cornerstonejs/tools';
import { Enums, eventTarget, getEnabledElement } from '@cornerstonejs/core';
import { MeasurementService } from '@ohif/core';
import { AllInOneMenu } from '@ohif/ui-next';
import { useViewportDialog } from '@ohif/ui-next';
import type { Types as csTypes } from '@cornerstonejs/core';
@@ -15,12 +13,11 @@ import CinePlayer from '../components/CinePlayer';
import type { Types } from '@ohif/core';
import OHIFViewportActionCorners from '../components/OHIFViewportActionCorners';
import { getWindowLevelActionMenu } from '../components/WindowLevelActionMenu/getWindowLevelActionMenu';
import { getViewportDataOverlaySettingsMenu } from '../components/ViewportDataOverlaySettingMenu';
import { getViewportPresentations } from '../utils/presentations/getViewportPresentations';
import { useSynchronizersStore } from '../stores/useSynchronizersStore';
import ActiveViewportBehavior from '../utils/ActiveViewportBehavior';
import { WITH_NAVIGATION } from '../services/ViewportService/CornerstoneViewportService';
import { useViewportActionCorners } from '../hooks';
const STACK = 'stack';
@@ -110,7 +107,23 @@ const OHIFCornerstoneViewport = React.memo(
cornerstoneViewportService.resize();
setImageScrollBarHeight();
}
}, [elementRef]);
}, [elementRef, cornerstoneViewportService, setImageScrollBarHeight]);
useEffect(() => {
const element = elementRef.current;
if (!element) {
return;
}
const resizeObserver = new ResizeObserver(onResize);
resizeObserver.observe(element);
// Cleanup function
return () => {
resizeObserver.unobserve(element);
resizeObserver.disconnect();
};
}, [onResize]);
const cleanUpServices = useCallback(
viewportInfo => {
@@ -306,52 +319,14 @@ const OHIFCornerstoneViewport = React.memo(
};
}, [displaySets, elementRef, viewportId, isJumpToMeasurementDisabled, servicesManager]);
// Set up the window level action menu in the viewport action corners.
useEffect(() => {
const windowLevelActionMenu = customizationService.getCustomization(
'viewportActionMenu.windowLevelActionMenu'
);
const segmentationOverlay = customizationService.getCustomization(
'viewportActionMenu.segmentationOverlay'
);
if (windowLevelActionMenu?.enabled) {
viewportActionCornersService.addComponent({
viewportId,
id: 'windowLevelActionMenu',
component: getWindowLevelActionMenu({
viewportId,
element: elementRef.current,
displaySets,
servicesManager,
commandsManager,
location: windowLevelActionMenu.location,
verticalDirection: AllInOneMenu.VerticalDirection.TopToBottom,
horizontalDirection: AllInOneMenu.HorizontalDirection.RightToLeft,
}),
location: windowLevelActionMenu.location,
});
}
if (segmentationOverlay?.enabled) {
viewportActionCornersService.addComponent({
viewportId,
id: 'segmentation',
component: getViewportDataOverlaySettingsMenu({
viewportId,
element: elementRef.current,
displaySets,
servicesManager,
commandsManager,
location: segmentationOverlay.location,
}),
location: segmentationOverlay.location,
});
}
}, [displaySets, viewportId, viewportActionCornersService, servicesManager, commandsManager]);
const { ref: resizeRef } = useResizeDetector({
onResize,
// Set up the window level action menu in the viewport action corners using the custom hook
useViewportActionCorners({
viewportId,
elementRef,
displaySets,
viewportActionCornersService,
customizationService,
commandsManager,
});
const Notification = customizationService.getCustomization('ui.notificationComponent');
@@ -364,10 +339,7 @@ const OHIFCornerstoneViewport = React.memo(
style={{ height: '100%', width: '100%' }}
onContextMenu={e => e.preventDefault()}
onMouseDown={e => e.preventDefault()}
ref={el => {
resizeRef.current = el;
elementRef.current = el;
}}
ref={elementRef}
></div>
<CornerstoneOverlays
viewportId={viewportId}
@@ -516,22 +488,18 @@ OHIFCornerstoneViewport.displayName = 'OHIFCornerstoneViewport';
function areEqual(prevProps, nextProps) {
if (nextProps.needsRerendering) {
console.debug('OHIFCornerstoneViewport: Rerender caused by: needsRerendering');
return false;
}
if (prevProps.displaySets.length !== nextProps.displaySets.length) {
console.debug('OHIFCornerstoneViewport: Rerender caused by: displaySets length change');
return false;
}
if (prevProps.viewportOptions.orientation !== nextProps.viewportOptions.orientation) {
console.debug('OHIFCornerstoneViewport: Rerender caused by: orientation change');
return false;
}
if (prevProps.viewportOptions.toolGroupId !== nextProps.viewportOptions.toolGroupId) {
console.debug('OHIFCornerstoneViewport: Rerender caused by: toolGroupId change');
return false;
}
@@ -539,12 +507,10 @@ function areEqual(prevProps, nextProps) {
nextProps.viewportOptions.viewportType &&
prevProps.viewportOptions.viewportType !== nextProps.viewportOptions.viewportType
) {
console.debug('OHIFCornerstoneViewport: Rerender caused by: viewportType change');
return false;
}
if (nextProps.viewportOptions.needsRerendering) {
console.debug('OHIFCornerstoneViewport: Rerender caused by: viewportOptions.needsRerendering');
return false;
}
@@ -552,7 +518,6 @@ function areEqual(prevProps, nextProps) {
const nextDisplaySets = nextProps.displaySets;
if (prevDisplaySets.length !== nextDisplaySets.length) {
console.debug('OHIFCornerstoneViewport: Rerender caused by: displaySets length mismatch');
return false;
}
@@ -565,13 +530,11 @@ function areEqual(prevProps, nextProps) {
);
if (!foundDisplaySet) {
console.debug('OHIFCornerstoneViewport: Rerender caused by: displaySet not found');
return false;
}
// check they contain the same image
if (foundDisplaySet.images?.length !== prevDisplaySet.images?.length) {
console.debug('OHIFCornerstoneViewport: Rerender caused by: images length mismatch');
return false;
}
@@ -579,7 +542,6 @@ function areEqual(prevProps, nextProps) {
if (foundDisplaySet.images?.length) {
for (let j = 0; j < foundDisplaySet.images.length; j++) {
if (foundDisplaySet.images[j].imageId !== prevDisplaySet.images[j].imageId) {
console.debug('OHIFCornerstoneViewport: Rerender caused by: imageId mismatch');
return false;
}
}
@@ -63,7 +63,7 @@ function CornerstoneImageScrollbar({
}
const { viewportType } = viewportData;
const eventId =
(viewportType === Enums.ViewportType.STACK && Enums.Events.STACK_VIEWPORT_SCROLL) ||
(viewportType === Enums.ViewportType.STACK && Enums.Events.STACK_NEW_IMAGE) ||
(viewportType === Enums.ViewportType.ORTHOGRAPHIC && Enums.Events.VOLUME_NEW_IMAGE) ||
Enums.Events.IMAGE_RENDERED;
@@ -72,11 +72,11 @@ function CornerstoneImageScrollbar({
if (!viewport || viewport instanceof VolumeViewport3D) {
return;
}
const { imageIndex, newImageIdIndex = imageIndex } = event.detail;
const { imageIndex, newImageIdIndex = imageIndex, imageIdIndex } = event.detail;
const numberOfSlices = viewport.getNumberOfSlices();
// find the index of imageId in the imageIds
setImageSliceData({
imageIndex: newImageIdIndex,
imageIndex: newImageIdIndex ?? imageIdIndex,
numberOfSlices,
});
};
+52 -8
View File
@@ -3,6 +3,7 @@ import {
StackViewport,
VolumeViewport,
utilities as csUtils,
Enums as CoreEnums,
Types as CoreTypes,
BaseVolumeViewport,
} from '@cornerstonejs/core';
@@ -248,7 +249,7 @@ function commandsModule({
},
updateStoredPositionPresentation: ({
viewportId,
displaySetInstanceUID,
displaySetInstanceUIDs,
referencedImageId,
options,
}) => {
@@ -256,11 +257,24 @@ function commandsModule({
const { positionPresentationStore, setPositionPresentation, getPositionPresentationId } =
usePositionPresentationStore.getState();
// Look inside positionPresentationStore and find the key that includes the displaySetInstanceUID
// Look inside positionPresentationStore and find the key that includes ALL the displaySetInstanceUIDs
// and the value has viewportId as activeViewportId.
const previousReferencedDisplaySetStoreKey = Object.entries(positionPresentationStore).find(
([key, value]) => key.includes(displaySetInstanceUID) && value.viewportId === viewportId
)?.[0];
let previousReferencedDisplaySetStoreKey;
if (
displaySetInstanceUIDs &&
Array.isArray(displaySetInstanceUIDs) &&
displaySetInstanceUIDs.length > 0
) {
previousReferencedDisplaySetStoreKey = Object.entries(positionPresentationStore).find(
([key, value]) => {
return (
displaySetInstanceUIDs.every(uid => key.includes(uid)) &&
value.viewportId === viewportId
);
}
)?.[0];
}
// Create presentation data with referencedImageId and options if provided
const presentationData = referencedImageId
@@ -282,7 +296,7 @@ function commandsModule({
// so we need to grab the positionPresentationId directly from the store,
// Todo: this is really hacky, we should have a better way for this
const positionPresentationId = getPositionPresentationId({
displaySetInstanceUIDs: [displaySetInstanceUID],
displaySetInstanceUIDs,
viewportId,
});
@@ -719,7 +733,7 @@ function commandsModule({
return;
}
if (!toolGroup.hasTool(toolName)) {
if (!toolGroup?.hasTool(toolName)) {
return;
}
@@ -1019,7 +1033,7 @@ function commandsModule({
const toolGroup = toolGroupService.getToolGroupForViewport(viewportId);
if (!toolGroup.hasTool(toolName)) {
if (!toolGroup?.hasTool(toolName)) {
return;
}
@@ -1189,6 +1203,9 @@ function commandsModule({
},
/**
* Creates a labelmap for the active viewport
*
* The created labelmap will be registered as a display set and also added
* as a segmentation representation to the viewport.
*/
createLabelmapForViewport: async ({ viewportId, options = {} }) => {
const { viewportGridService, displaySetService, segmentationService } =
@@ -1209,6 +1226,7 @@ function commandsModule({
const displaySet = displaySetService.getDisplaySetByUID(displaySetInstanceUID);
// This will create the segmentation and register it as a display set
const generatedSegmentationId = await segmentationService.createLabelmapForDisplaySet(
displaySet,
{
@@ -1225,6 +1243,7 @@ function commandsModule({
}
);
// Also add the segmentation representation to the viewport
await segmentationService.addSegmentationRepresentation(viewportId, {
segmentationId,
type: Enums.SegmentationRepresentations.Labelmap,
@@ -1741,6 +1760,30 @@ function commandsModule({
});
});
},
setViewportOrientation: ({ viewportId, orientation }) => {
const viewport = cornerstoneViewportService.getCornerstoneViewport(viewportId);
if (!viewport || viewport.type !== CoreEnums.ViewportType.ORTHOGRAPHIC) {
console.warn('Orientation can only be set on volume viewports');
return;
}
// Get display sets for this viewport to verify at least one is reconstructable
const displaySetUIDs = viewportGridService.getDisplaySetsUIDsForViewport(viewportId);
const displaySets = displaySetUIDs.map(uid => displaySetService.getDisplaySetByUID(uid));
if (!displaySets.some(ds => ds.isReconstructable)) {
console.warn('Cannot change orientation: No reconstructable display sets in viewport');
return;
}
viewport.setOrientation(orientation);
viewport.render();
// update the orientation in the viewport info
const viewportInfo = cornerstoneViewportService.getViewportInfo(viewportId);
viewportInfo.setOrientation(orientation);
},
};
const definitions = {
@@ -2021,6 +2064,7 @@ function commandsModule({
decreaseBrushSize: actions.decreaseBrushSize,
addNewSegment: actions.addNewSegment,
loadSegmentationDisplaySetsForViewport: actions.loadSegmentationDisplaySetsForViewport,
setViewportOrientation: actions.setViewportOrientation,
};
return {
@@ -1,6 +1,7 @@
import React from 'react';
import { useViewportActionCornersContext } from '../contextProviders/ViewportActionCornersProvider';
import { useSystem } from '@ohif/core';
import { useViewportGrid } from '@ohif/ui-next';
export type OHIFViewportActionCornersProps = {
viewportId: string;
@@ -9,8 +10,13 @@ export type OHIFViewportActionCornersProps = {
function OHIFViewportActionCorners({ viewportId }: OHIFViewportActionCornersProps) {
const { servicesManager } = useSystem();
const [viewportActionCornersState] = useViewportActionCornersContext();
const [viewportGrid] = useViewportGrid();
const isActiveViewport = viewportGrid.activeViewportId === viewportId;
const ViewportActionCorners =
servicesManager.services.customizationService.getCustomization('ui.viewportActionCorner');
if (!viewportActionCornersState[viewportId]) {
return null;
}
@@ -18,7 +24,8 @@ function OHIFViewportActionCorners({ viewportId }: OHIFViewportActionCornersProp
return (
<ViewportActionCorners
cornerComponents={viewportActionCornersState[viewportId]}
></ViewportActionCorners>
isActiveViewport={isActiveViewport}
/>
);
}
@@ -0,0 +1,478 @@
import React, { useState } from 'react';
import {
Button,
Icons,
DropdownMenu,
DropdownMenuTrigger,
DropdownMenuContent,
DropdownMenuItem,
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
Switch,
} from '@ohif/ui-next';
import { useSystem } from '@ohif/core';
import { useViewportDisplaySets } from '../../hooks/useViewportDisplaySets';
function ViewportDataOverlayMenu({ viewportId }: withAppTypes<{ viewportId: string }>) {
const { commandsManager, servicesManager } = useSystem();
const [pendingForegrounds, setPendingForegrounds] = useState<string[]>([]);
const [pendingSegmentations, setPendingSegmentations] = useState<string[]>([]);
const [thresholdOpacityEnabled, setThresholdOpacityEnabled] = useState(false);
const { hangingProtocolService } = servicesManager.services;
const {
backgroundDisplaySet,
potentialOverlayDisplaySets,
potentialForegroundDisplaySets,
potentialBackgroundDisplaySets,
overlayDisplaySets,
foregroundDisplaySets,
} = useViewportDisplaySets(viewportId);
/**
* Change the background display set
*/
const handleBackgroundSelection = (newBackgroundDisplaySet: AppTypes.DisplaySet) => {
const updatedViewports = hangingProtocolService.getViewportsRequireUpdate(
viewportId,
newBackgroundDisplaySet.displaySetInstanceUID
);
commandsManager.run('setDisplaySetsForViewports', {
viewportsToUpdate: updatedViewports,
});
};
/**
* Handle threshold and opacity toggle
*/
const handleThresholdOpacityToggle = (checked: boolean) => {
setThresholdOpacityEnabled(checked);
if (foregroundDisplaySets.length > 0) {
// Example implementation of threshold/opacity adjustment
commandsManager.runCommand('setForegroundThresholdOpacity', {
viewportId,
enabled: checked,
});
}
};
/**
* Replace a display set layer with a new one
*/
const handleReplaceDisplaySetLayer = (
currentDisplaySetInstanceUID: string,
newDisplaySetInstanceUID: string
) => {
// Remove current display set
commandsManager.runCommand('removeDisplaySetLayer', {
viewportId,
displaySetInstanceUID: currentDisplaySetInstanceUID,
});
setTimeout(() => {
commandsManager.runCommand('addDisplaySetAsLayer', {
viewportId,
displaySetInstanceUID: newDisplaySetInstanceUID,
});
}, 0);
};
/**
* Remove a display set layer
*/
const handleRemoveDisplaySetLayer = (displaySetInstanceUID: string) => {
commandsManager.runCommand('removeDisplaySetLayer', {
viewportId,
displaySetInstanceUID,
});
};
/**
* Add a display set as a layer
*/
const handleAddDisplaySetAsLayer = (displaySetInstanceUID: string) => {
commandsManager.runCommand('addDisplaySetAsLayer', {
viewportId,
displaySetInstanceUID,
});
};
/**
* Handle overlay display set selection change
*/
const handleOverlaySelectionChange = (
currentDisplaySet: AppTypes.DisplaySet,
newDisplaySetInstanceUID: string
) => {
if (newDisplaySetInstanceUID === currentDisplaySet.displaySetInstanceUID) {
return;
}
// Find the selected display set
const selectedDisplaySet = potentialOverlayDisplaySets.find(
ds => ds.displaySetInstanceUID === newDisplaySetInstanceUID
);
if (selectedDisplaySet) {
handleReplaceDisplaySetLayer(
currentDisplaySet.displaySetInstanceUID,
selectedDisplaySet.displaySetInstanceUID
);
}
};
/**
* Handle foreground display set selection change
*/
const handleForegroundSelectionChange = (
currentDisplaySet: AppTypes.DisplaySet,
newDisplaySetInstanceUID: string
) => {
if (newDisplaySetInstanceUID === currentDisplaySet.displaySetInstanceUID) {
return;
}
// Find the selected display set
const selectedDisplaySet = potentialForegroundDisplaySets.find(
ds => ds.displaySetInstanceUID === newDisplaySetInstanceUID
);
if (selectedDisplaySet) {
handleReplaceDisplaySetLayer(
currentDisplaySet.displaySetInstanceUID,
selectedDisplaySet.displaySetInstanceUID
);
}
};
/**
* Handle pending segmentation selection
*/
const handlePendingSegmentationSelection = (pendingId: string, displaySetInstanceUID: string) => {
const selectedDisplaySet = potentialOverlayDisplaySets.find(
ds => ds.displaySetInstanceUID === displaySetInstanceUID
);
if (selectedDisplaySet) {
handleAddDisplaySetAsLayer(selectedDisplaySet.displaySetInstanceUID);
// Remove this pending segmentation from the list
setPendingSegmentations(pendingSegmentations.filter(id => id !== pendingId));
}
};
/**
* Handle pending foreground selection
*/
const handlePendingForegroundSelection = (pendingId: string, displaySetInstanceUID: string) => {
const selectedDisplaySet = potentialForegroundDisplaySets.find(
ds => ds.displaySetInstanceUID === displaySetInstanceUID
);
if (selectedDisplaySet) {
handleAddDisplaySetAsLayer(selectedDisplaySet.displaySetInstanceUID);
// Remove this pending foreground from the list
setPendingForegrounds(pendingForegrounds.filter(id => id !== pendingId));
}
};
return (
<div className="bg-popover flex h-full w-[275px] flex-col rounded rounded-md p-1.5">
{/* Top buttons row */}
<div className={`flex`}>
<Button
variant="ghost"
className="text-primary flex items-center p-1"
onClick={() => {
// Add a new pending foreground slot with a unique ID
setPendingForegrounds([...pendingForegrounds, `pending-${Date.now()}`]);
}}
disabled={potentialForegroundDisplaySets.length === 0}
>
<Icons.Plus className="h-4 w-4" />
Foreground
</Button>
<Button
variant="ghost"
className="text-primary ml-2 flex items-center"
disabled={potentialOverlayDisplaySets.length === 0}
onClick={() => {
setPendingSegmentations([...pendingSegmentations, `seg-${Date.now()}`]);
}}
>
<Icons.Plus className="h-4 w-4" />
Segmentation
</Button>
</div>
<div className="">
{/* Overlays Segmentation section */}
<div className="my-2 ml-1">
{overlayDisplaySets.map((displaySet, index) => (
<div
key={displaySet.displaySetInstanceUID}
className="mb-1 flex items-center"
>
<Icons.LayerSegmentation className="text-muted-foreground mr-1 h-6 w-6 flex-shrink-0" />
<Select
value={displaySet.displaySetInstanceUID}
onValueChange={value => handleOverlaySelectionChange(displaySet, value)}
>
<SelectTrigger className="flex-1">
<SelectValue>{displaySet.label?.toUpperCase()}</SelectValue>
</SelectTrigger>
<SelectContent>
{/* Include both potential overlays and the current overlay */}
<SelectItem
key={displaySet.displaySetInstanceUID}
value={displaySet.displaySetInstanceUID}
>
{displaySet.label}
</SelectItem>
{potentialOverlayDisplaySets.map(item => (
<SelectItem
key={item.displaySetInstanceUID}
value={item.displaySetInstanceUID}
>
{item.label}
</SelectItem>
))}
</SelectContent>
</Select>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
variant="ghost"
size="icon"
className="ml-2 flex-shrink-0"
>
<Icons.More className="h-4 w-4" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="start">
<DropdownMenuItem
onClick={() => handleRemoveDisplaySetLayer(displaySet.displaySetInstanceUID)}
>
Remove
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</div>
))}
{pendingSegmentations.map(pendingId => (
<div
key={pendingId}
className="mb-2 flex items-center"
>
<Icons.LayerSegmentation className="text-muted-foreground mr-1 h-6 w-6 flex-shrink-0" />
<Select
value=""
onValueChange={value => handlePendingSegmentationSelection(pendingId, value)}
>
<SelectTrigger className="flex-1">
<SelectValue placeholder="SELECT A SEGMENTATION" />
</SelectTrigger>
<SelectContent>
{potentialOverlayDisplaySets.map(item => (
<SelectItem
key={item.displaySetInstanceUID}
value={item.displaySetInstanceUID}
>
{item.label}
</SelectItem>
))}
</SelectContent>
</Select>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
variant="ghost"
size="icon"
className="ml-2 flex-shrink-0"
>
<Icons.More className="h-4 w-4" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="start">
<DropdownMenuItem
onClick={() => {
// Remove this pending segmentation
setPendingSegmentations(pendingSegmentations.filter(id => id !== pendingId));
}}
>
Cancel
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</div>
))}
</div>
{/* Foregrounds section */}
<div className="my-2 px-1">
{foregroundDisplaySets.map((displaySet, index) => (
<div
key={displaySet.displaySetInstanceUID}
className="mb-1 flex items-center"
>
<Icons.LayerForeground className="text-muted-foreground mr-1 h-6 w-6 flex-shrink-0" />
<Select
value={displaySet.displaySetInstanceUID}
onValueChange={value => handleForegroundSelectionChange(displaySet, value)}
>
<SelectTrigger className="flex-1">
<SelectValue>{displaySet.label?.toUpperCase()}</SelectValue>
</SelectTrigger>
<SelectContent>
{/* Include both potential foregrounds and the current foreground */}
<SelectItem
key={displaySet.displaySetInstanceUID}
value={displaySet.displaySetInstanceUID}
>
{displaySet.label}
</SelectItem>
{potentialForegroundDisplaySets.map(item => (
<SelectItem
key={item.displaySetInstanceUID}
value={item.displaySetInstanceUID}
>
{item.label}
</SelectItem>
))}
</SelectContent>
</Select>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
variant="ghost"
size="icon"
className="ml-2 flex-shrink-0"
>
<Icons.More className="h-4 w-4" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="start">
<DropdownMenuItem
onClick={() => handleRemoveDisplaySetLayer(displaySet.displaySetInstanceUID)}
>
Remove
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</div>
))}
{pendingForegrounds.map(pendingId => (
<div
key={pendingId}
className="mb-2 flex items-center"
>
<Icons.LayerForeground className="text-muted-foreground mr-1 h-6 w-6 flex-shrink-0" />
<Select
value=""
onValueChange={value => handlePendingForegroundSelection(pendingId, value)}
>
<SelectTrigger className="flex-1">
<SelectValue placeholder="SELECT A FOREGROUND" />
</SelectTrigger>
<SelectContent>
{potentialForegroundDisplaySets.map(item => (
<SelectItem
key={item.displaySetInstanceUID}
value={item.displaySetInstanceUID}
>
{item.label}
</SelectItem>
))}
</SelectContent>
</Select>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
variant="ghost"
size="icon"
className="ml-2 flex-shrink-0"
>
<Icons.More className="h-4 w-4" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="start">
<DropdownMenuItem
onClick={() => {
// Remove this pending foreground
setPendingForegrounds(pendingForegrounds.filter(id => id !== pendingId));
}}
>
Cancel
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</div>
))}
</div>
{/* Background section */}
<div className="mt-1 mb-1 flex items-center px-1">
<Icons.LayerBackground className="text-muted-foreground mr-1 h-6 w-6 flex-shrink-0" />
<Select
value={backgroundDisplaySet.displaySetInstanceUID}
onValueChange={value => {
const selectedDisplaySet = potentialBackgroundDisplaySets.find(
ds => ds.displaySetInstanceUID === value
);
if (selectedDisplaySet) {
handleBackgroundSelection(selectedDisplaySet);
}
}}
>
<SelectTrigger className="flex-1">
<SelectValue>
{(
backgroundDisplaySet.SeriesDescription ||
backgroundDisplaySet.label ||
'background'
).toUpperCase()}
</SelectValue>
</SelectTrigger>
<SelectContent>
{potentialBackgroundDisplaySets.map(displaySet => (
<SelectItem
key={displaySet.displaySetInstanceUID}
value={displaySet.displaySetInstanceUID}
>
{displaySet.label}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
</div>
{/* {foregroundDisplaySets.length > 0 && (
<div className="mt-1 ml-7">
<div className="flex items-center">
<Switch
id="threshold-opacity-switch"
className="mr-2"
checked={thresholdOpacityEnabled}
onCheckedChange={handleThresholdOpacityToggle}
/>
<label
htmlFor="threshold-opacity-switch"
className="text-muted-foreground cursor-pointer text-sm"
onClick={() => setThresholdOpacityEnabled(!thresholdOpacityEnabled)}
>
Control threshold & opacity
</label>
</div>
</div>
)} */}
</div>
);
}
export default ViewportDataOverlayMenu;
@@ -0,0 +1,62 @@
import React, { ReactNode } from 'react';
import {
Button,
cn,
Icons,
Popover,
PopoverContent,
PopoverTrigger,
useViewportGrid,
} from '@ohif/ui-next';
import ViewportDataOverlayMenu from './ViewportDataOverlayMenu';
import classNames from 'classnames';
import { useSystem } from '@ohif/core';
export function ViewportDataOverlayMenuWrapper({
viewportId,
displaySets,
location,
}: withAppTypes<{
viewportId: string;
element: HTMLElement;
}>): ReactNode {
const { servicesManager } = useSystem();
const { viewportActionCornersService } = servicesManager.services;
const [viewportGrid] = useViewportGrid();
const { align, side } = viewportActionCornersService.getAlignAndSide(location);
return (
<Popover>
<PopoverTrigger
asChild
className="flex items-center justify-center"
>
<Button
variant="ghost"
size="icon"
className={cn(
viewportGrid.activeViewportId === viewportId
? 'visible'
: 'invisible group-hover/pane:visible'
)}
>
<Icons.ViewportViews className={classNames('text-highlight')} />
</Button>
</PopoverTrigger>
<PopoverContent
className="border-none bg-transparent p-0 shadow-none"
side={side}
align={align}
alignOffset={0}
sideOffset={5}
>
<ViewportDataOverlayMenu
className="w-full"
viewportId={viewportId}
displaySets={displaySets}
/>
</PopoverContent>
</Popover>
);
}
@@ -1,136 +0,0 @@
import React, { useEffect, useState } from 'react';
import { Button, Icons, Separator } from '@ohif/ui-next';
import { SegmentationRepresentations } from '@cornerstonejs/tools/enums';
function ViewportSegmentationMenu({
viewportId,
servicesManager,
}: withAppTypes<{ viewportId: string }>) {
const { segmentationService } = servicesManager.services;
const [activeSegmentations, setActiveSegmentations] = useState([]);
const [availableSegmentations, setAvailableSegmentations] = useState([]);
useEffect(() => {
const updateSegmentations = () => {
const active = segmentationService.getSegmentationRepresentations(viewportId);
setActiveSegmentations(active);
const all = segmentationService.getSegmentations();
const available = all.filter(
seg => !active.some(activeSeg => activeSeg.segmentationId === seg.segmentationId)
);
setAvailableSegmentations(available);
};
updateSegmentations();
const subscriptions = [
segmentationService.EVENTS.SEGMENTATION_MODIFIED,
segmentationService.EVENTS.SEGMENTATION_REMOVED,
segmentationService.EVENTS.SEGMENTATION_REPRESENTATION_MODIFIED,
].map(event => segmentationService.subscribe(event, updateSegmentations));
return () => {
subscriptions.forEach(subscription => subscription.unsubscribe());
};
}, [segmentationService, viewportId]);
const toggleSegmentationRepresentationVisibility = (
segmentationId,
type = SegmentationRepresentations.Labelmap
) => {
segmentationService.toggleSegmentationRepresentationVisibility(viewportId, {
segmentationId,
type,
});
};
const addSegmentationToViewport = segmentationId => {
segmentationService.addSegmentationRepresentation(viewportId, { segmentationId });
};
const removeSegmentationFromViewport = segmentationId => {
segmentationService.removeSegmentationRepresentations(viewportId, {
segmentationId,
});
};
return (
<div className="bg-muted flex h-full w-[262px] flex-col rounded p-3">
<span className="text-muted-foreground mb-2 text-xs font-semibold">Current Viewport</span>
<ul className="space-y-1">
{activeSegmentations.map(segmentation => (
<li
key={segmentation.id}
className="flex items-center text-sm"
>
<Button
variant="ghost"
size="icon"
className="text-muted-foreground mr-2"
onClick={() => removeSegmentationFromViewport(segmentation.segmentationId)}
>
<Icons.Minus className="h-6 w-6" />
</Button>
<span className="text-foreground flex-grow">{segmentation.label}</span>
{segmentation.visible ? (
<Button
variant="ghost"
size="icon"
className="text-muted-foreground"
onClick={() =>
toggleSegmentationRepresentationVisibility(
segmentation.segmentationId,
segmentation.type
)
}
>
<Icons.Hide className="h-6 w-6" />
</Button>
) : (
<Button
variant="ghost"
size="icon"
className="text-muted-foreground"
onClick={() =>
toggleSegmentationRepresentationVisibility(
segmentation.segmentationId,
segmentation.type
)
}
>
<Icons.Show className="h-6 w-6" />
</Button>
)}
</li>
))}
</ul>
{availableSegmentations.length > 0 && (
<>
<Separator className="bg-input mb-3" />
<span className="text-muted-foreground mb-2 text-xs font-semibold">Available</span>
<ul className="space-y-1">
{availableSegmentations.map(({ segmentationId, label }) => (
<li
key={segmentationId}
className="flex items-center text-sm"
>
<Button
variant="ghost"
size="icon"
className="text-muted-foreground mr-2"
onClick={() => addSegmentationToViewport(segmentationId)}
>
<Icons.Plus className="h-6 w-6" />
</Button>
<span className="text-foreground/60">{label}</span>
</li>
))}
</ul>
</>
)}
</div>
);
}
export default ViewportSegmentationMenu;
@@ -1,83 +0,0 @@
import React, { ReactNode, useEffect, useState } from 'react';
import { Button, Icons, Popover, PopoverContent, PopoverTrigger } from '@ohif/ui-next';
import ViewportSegmentationMenu from './ViewportSegmentationMenu';
import classNames from 'classnames';
import { useSegmentations } from '../../hooks/useSegmentations';
export function ViewportSegmentationMenuWrapper({
viewportId,
displaySets,
servicesManager,
commandsManager,
location,
}: withAppTypes<{
viewportId: string;
element: HTMLElement;
}>): ReactNode {
const { viewportActionCornersService, viewportGridService } = servicesManager.services;
const segmentations = useSegmentations({ servicesManager });
const activeViewportId = viewportGridService.getActiveViewportId();
const isActiveViewport = viewportId === activeViewportId;
const { align, side } = getAlignAndSide(viewportActionCornersService, location);
if (!segmentations?.length) {
return null;
}
return (
<Popover>
<PopoverTrigger
asChild
className="flex items-center justify-center"
>
<Button
variant="ghost"
size="icon"
>
<Icons.ViewportViews
className={classNames(
'text-highlight',
isActiveViewport ? 'visible' : 'invisible group-hover/pane:visible'
)}
/>
</Button>
</PopoverTrigger>
<PopoverContent
className="border-none bg-transparent p-0 shadow-none"
side={side}
align={align}
alignOffset={-15}
sideOffset={5}
>
<ViewportSegmentationMenu
className="w-full"
viewportId={viewportId}
displaySets={displaySets}
servicesManager={servicesManager}
commandsManager={commandsManager}
/>
</PopoverContent>
</Popover>
);
}
const getAlignAndSide = (viewportActionCornersService, location) => {
const ViewportActionCornersLocations = viewportActionCornersService.LOCATIONS;
switch (location) {
case ViewportActionCornersLocations.topLeft:
return { align: 'start', side: 'bottom' };
case ViewportActionCornersLocations.topRight:
return { align: 'end', side: 'bottom' };
case ViewportActionCornersLocations.bottomLeft:
return { align: 'start', side: 'top' };
case ViewportActionCornersLocations.bottomRight:
return { align: 'end', side: 'top' };
default:
console.debug('Unknown location, defaulting to bottom-start');
return { align: 'start', side: 'bottom' };
}
};
@@ -1,5 +1,5 @@
import React, { ReactNode } from 'react';
import { ViewportSegmentationMenuWrapper } from './ViewportSegmentationMenuWrapper';
import { ViewportDataOverlayMenuWrapper } from './ViewportDataOverlayMenuWrapper';
export function getViewportDataOverlaySettingsMenu(
props: withAppTypes<{
@@ -7,5 +7,5 @@ export function getViewportDataOverlaySettingsMenu(
element: HTMLElement;
}>
): ReactNode {
return <ViewportSegmentationMenuWrapper {...props} />;
return <ViewportDataOverlayMenuWrapper {...props} />;
}
@@ -0,0 +1,156 @@
import { utilities as csUtils } from '@cornerstonejs/core';
export const DEFAULT_COLORMAP = 'hsv';
export const DEFAULT_OPACITY = 0.5;
export const DEFAULT_OPACITY_PERCENT = DEFAULT_OPACITY * 100;
export const DERIVED_OVERLAY_MODALITIES = ['SEG', 'RTSTRUCT'];
/**
* Get modality-specific color and opacity settings from the customization service
*/
export function getModalityOverlayColormap(customizationService, modality) {
const modalityOverlayDefaultColorMaps = customizationService?.getCustomization(
'cornerstone.modalityOverlayDefaultColorMaps'
) || { defaultSettings: {} };
return (
modalityOverlayDefaultColorMaps.defaultSettings[modality] || {
colormap: DEFAULT_COLORMAP,
opacity: DEFAULT_OPACITY,
}
);
}
/**
* Identifies display sets that can be used as overlays for a specific viewport.
*
* "Enhanced" display sets are those that:
* 1. Are not already in the viewport
* 2. Are evaluated for their ability to be overlaid onto the background display set
* 3. Have an "isOverlayable" flag indicating if they're compatible with the viewport
*
* A display set is considered overlayable when:
* - The background display set is reconstructable
* - The display set is not unsupported
* - The Frame of Reference matches the background display set
* - For non-derived modalities: background can be a volume and display set is either multiframe or valid volume
*
* @returns {Object} Object containing:
* - viewportDisplaySets: Display sets already in the viewport
* - enhancedDisplaySets: Other display sets with overlayability assessment
*/
export function getEnhancedDisplaySets({ viewportId, services }) {
const { displaySetService, viewportGridService } = services;
const displaySetsUIDs = viewportGridService.getDisplaySetsUIDsForViewport(viewportId);
const allDisplaySets = displaySetService.getActiveDisplaySets();
const otherDisplaySets = allDisplaySets.filter(
displaySet => !displaySetsUIDs.includes(displaySet.displaySetInstanceUID)
);
const viewportDisplaySets = displaySetsUIDs.map(displaySetUID =>
displaySetService.getDisplaySetByUID(displaySetUID)
);
const backgroundCanBeVolume = csUtils.isValidVolume(viewportDisplaySets[0].imageIds);
const backgroundDisplaySet = viewportDisplaySets[0];
const enhancedDisplaySets = otherDisplaySets.map(displaySet => {
if (!backgroundDisplaySet.isReconstructable) {
return {
...displaySet,
isOverlayable: false,
};
}
if (displaySet.unsupported) {
return {
...displaySet,
isOverlayable: false,
};
}
// Check if Frame of Reference matches
if (
displaySet.FrameOfReferenceUID &&
displaySet.FrameOfReferenceUID !== backgroundDisplaySet.FrameOfReferenceUID
) {
return {
...displaySet,
isOverlayable: false,
};
}
// Special handling for derived modalities
if (!DERIVED_OVERLAY_MODALITIES.includes(displaySet.Modality)) {
if (!backgroundCanBeVolume) {
return {
...displaySet,
isOverlayable: false,
};
}
const imageIds = displaySet.imageIds || displaySet.images?.map(image => image.imageId);
const isMultiframe = displaySet.isMultiFrame;
if (!isMultiframe && imageIds?.length > 0 && !csUtils.isValidVolume(imageIds)) {
return {
...displaySet,
isOverlayable: false,
};
}
}
return {
...displaySet,
isOverlayable: true,
};
});
return {
viewportDisplaySets,
enhancedDisplaySets,
};
}
/**
* Sort function: puts disabled items (isOverlayable: false) at the end
*/
export const sortByOverlayable = (a, b) => {
if (a.isOverlayable === b.isOverlayable) {
return 0;
}
return a.isOverlayable ? -1 : 1;
};
/**
* Create display set options based on modality and opacity settings
*/
export function createColormapOverlayDisplaySetOptions(displaySet, opacity, customizationService) {
if (displaySet.Modality === 'SEG') {
return {};
}
const modalitySettings = getModalityOverlayColormap(customizationService, displaySet.Modality);
return {
colormap: {
name: modalitySettings.colormap || DEFAULT_COLORMAP,
opacity: opacity / 100, // Convert from percentage to 0-1 range
},
};
}
/**
* Get segmentations that can be added as overlays to the viewport
*
* Note: This function is deprecated as we now use display sets for segmentations
*/
export function getAvailableSegmentations(segmentationService) {
const segmentations = segmentationService.getSegmentations() || [];
return segmentations.map(segmentation => ({
segmentationId: segmentation.segmentationId,
label: segmentation.label || 'Segmentation',
segments: segmentation.segments,
frameOfReferenceUID: segmentation.frameOfReferenceUID,
}));
}
@@ -0,0 +1,140 @@
import React from 'react';
import { Icons, useViewportGrid } from '@ohif/ui-next';
import { useSystem } from '@ohif/core';
import { Enums } from '@cornerstonejs/core';
import {
DropdownMenu,
DropdownMenuTrigger,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuLabel,
Button,
} from '@ohif/ui-next';
function ViewportOrientationMenu({ location }: withAppTypes<{ location?: string }>) {
const { servicesManager, commandsManager } = useSystem();
const [viewportGridState, viewportGridService] = useViewportGrid();
const { cornerstoneViewportService, displaySetService } = servicesManager.services;
const viewportId = viewportGridState.activeViewportId;
const handleOrientationChange = (orientation: string) => {
const viewportInfo = cornerstoneViewportService.getViewportInfo(viewportId);
const currentViewportType = viewportInfo?.getViewportType();
// Get the displaySets in this viewport
const displaySetUIDs = viewportGridService.getDisplaySetsUIDsForViewport(viewportId);
const displaySets = displaySetUIDs
.map(uid => displaySetService.getDisplaySetByUID(uid))
.filter(Boolean);
if (!displaySets.length) {
return;
}
// Check if at least one displaySet is reconstructable
const hasReconstructableDisplaySet = displaySets.some(ds => ds.isReconstructable);
if (!hasReconstructableDisplaySet) {
console.warn('Cannot change orientation: No reconstructable display sets in viewport');
return;
}
// Set the orientation enum based on the selected orientation
let orientationEnum;
switch (orientation.toLowerCase()) {
case 'axial':
orientationEnum = Enums.OrientationAxis.AXIAL;
break;
case 'sagittal':
orientationEnum = Enums.OrientationAxis.SAGITTAL;
break;
case 'coronal':
orientationEnum = Enums.OrientationAxis.CORONAL;
break;
default:
orientationEnum = Enums.OrientationAxis.ACQUISITION;
}
// If viewport is not already a volume type, we need to convert it
if (currentViewportType !== Enums.ViewportType.ORTHOGRAPHIC) {
// Configure the viewport to be a volume viewport with current display sets
const updatedViewport = {
viewportId,
displaySetInstanceUIDs: displaySetUIDs,
viewportOptions: {
viewportType: Enums.ViewportType.ORTHOGRAPHIC,
orientation: orientationEnum,
},
displaySetOptions: displaySetUIDs.map(() => ({})),
};
// Update the viewport to be a volume viewport
commandsManager.run('setDisplaySetsForViewports', {
viewportsToUpdate: [updatedViewport],
});
} else {
// Set the viewport orientation
commandsManager.runCommand('setViewportOrientation', {
viewportId,
orientation: orientationEnum,
});
}
};
const { viewportActionCornersService } = servicesManager.services;
const displaySetUIDs = viewportGridService.getDisplaySetsUIDsForViewport(viewportId);
const displaySets = displaySetUIDs
.map(uid => displaySetService.getDisplaySetByUID(uid))
.filter(Boolean);
if (!displaySets.length) {
return null;
}
const hasReconstructableDisplaySet = displaySets.some(ds => ds.isReconstructable);
// Get proper alignment and side based on the location
let align = 'center';
let side = 'bottom';
if (location !== undefined) {
const positioning = viewportActionCornersService.getAlignAndSide(location);
align = positioning.align;
side = positioning.side;
}
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
variant="ghost"
size="icon"
className="text-highlight"
disabled={!hasReconstructableDisplaySet}
>
<Icons.OrientationSwitch />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent
className="min-w-[160px]"
align={align}
side={side}
sideOffset={5}
>
<DropdownMenuLabel className="-ml-1">Orientation</DropdownMenuLabel>
<DropdownMenuItem onClick={() => handleOrientationChange('axial')}>Axial</DropdownMenuItem>
<DropdownMenuItem onClick={() => handleOrientationChange('sagittal')}>
Sagittal
</DropdownMenuItem>
<DropdownMenuItem onClick={() => handleOrientationChange('coronal')}>
Coronal
</DropdownMenuItem>
<DropdownMenuItem onClick={() => handleOrientationChange('acquisition')}>
Acquisition
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
);
}
export default ViewportOrientationMenu;
@@ -0,0 +1,27 @@
import React, { ReactNode } from 'react';
import { useViewportGrid } from '@ohif/ui-next';
import { cn } from '@ohif/ui-next';
import ViewportOrientationMenu from './ViewportOrientationMenu';
export function ViewportOrientationMenuWrapper({
viewportId,
location,
}: withAppTypes<{
viewportId: string;
element: HTMLElement;
location: string;
}>): ReactNode {
const [viewportGrid] = useViewportGrid();
const isActiveViewport = viewportId === viewportGrid.activeViewportId;
return (
<div
className={cn(
'flex justify-end',
isActiveViewport ? 'visible' : 'invisible group-hover/pane:visible'
)}
>
<ViewportOrientationMenu location={location} />
</div>
);
}
@@ -0,0 +1,14 @@
import React, { ReactNode } from 'react';
import { ViewportOrientationMenuWrapper } from './ViewportOrientationMenuWrapper';
export function getViewportOrientationMenu(
props: withAppTypes<{
viewportId: string;
element: HTMLElement;
location: string;
}>
): ReactNode {
return <ViewportOrientationMenuWrapper {...props} />;
}
export * from './ViewportOrientationMenu';
@@ -1,18 +1,18 @@
import React, { ReactElement, useCallback, useEffect, useState } from 'react';
import { Switch } from '@ohif/ui-next';
import { StackViewport, VolumeViewport } from '@cornerstonejs/core';
import { StackViewport } from '@cornerstonejs/core';
import { ColorbarProps } from '../../types/Colorbar';
import { utilities } from '@cornerstonejs/core';
import { useSystem } from '@ohif/core';
export function setViewportColorbar(
viewportId,
displaySets,
commandsManager,
servicesManager: AppTypes.ServicesManager,
colorbarOptions
) {
const { cornerstoneViewportService } = servicesManager.services;
const viewport = cornerstoneViewportService.getCornerstoneViewport(viewportId);
const { cornerstoneViewportService, viewportGridService } = servicesManager.services;
const displaySetInstanceUIDs = viewportGridService.getDisplaySetsUIDsForViewport(viewportId);
const viewportInfo = cornerstoneViewportService.getViewportInfo(viewportId);
const backgroundColor = viewportInfo.getViewportOptions().background;
@@ -32,36 +32,18 @@ export function setViewportColorbar(
};
}
const displaySetInstanceUIDs = [];
if (viewport instanceof StackViewport) {
displaySetInstanceUIDs.push(viewportId);
}
if (viewport instanceof VolumeViewport) {
displaySets.forEach(ds => {
displaySetInstanceUIDs.push(ds.displaySetInstanceUID);
});
}
commandsManager.run({
commandName: 'toggleViewportColorbar',
commandOptions: {
viewportId,
options: colorbarOptions,
displaySetInstanceUIDs,
},
context: 'CORNERSTONE',
commandsManager.run('toggleViewportColorbar', {
viewportId,
options: colorbarOptions,
displaySetInstanceUIDs,
});
}
export function Colorbar({
viewportId,
displaySets,
commandsManager,
servicesManager,
colorbarProperties,
}: withAppTypes<ColorbarProps>): ReactElement {
const { servicesManager, commandsManager } = useSystem();
const { colorbarService } = servicesManager.services;
const {
width: colorbarWidth,
@@ -73,7 +55,7 @@ export function Colorbar({
const [showColorbar, setShowColorbar] = useState(colorbarService.hasColorbar(viewportId));
const onSetColorbar = useCallback(() => {
setViewportColorbar(viewportId, displaySets, commandsManager, servicesManager, {
setViewportColorbar(viewportId, commandsManager, servicesManager, {
viewportId,
colormaps,
ticks: {
@@ -3,14 +3,10 @@ import { AllInOneMenu, Switch, Tabs, TabsList, TabsTrigger } from '@ohif/ui-next
import { StackViewport, Types } from '@cornerstonejs/core';
import { ColormapProps } from '../../types/Colormap';
import { useSystem } from '@ohif/core';
export function Colormap({
colormaps,
viewportId,
displaySets,
commandsManager,
servicesManager,
}: ColormapProps): ReactElement {
export function Colormap({ colormaps, viewportId, displaySets }: ColormapProps): ReactElement {
const { servicesManager, commandsManager } = useSystem();
const { cornerstoneViewportService } = servicesManager.services;
const [activeDisplaySet, setActiveDisplaySet] = useState(displaySets[0]);
@@ -1,13 +1,10 @@
import React, { ReactElement, useState, useEffect, useCallback } from 'react';
import { VolumeLightingProps } from '../../types/ViewportPresets';
import { Numeric } from '@ohif/ui-next';
import { useSystem } from '@ohif/core';
export function VolumeLighting({
servicesManager,
commandsManager,
viewportId,
hasShade,
}: VolumeLightingProps): ReactElement {
export function VolumeLighting({ viewportId, hasShade }: VolumeLightingProps): ReactElement {
const { servicesManager, commandsManager } = useSystem();
const { cornerstoneViewportService } = servicesManager.services;
const [lightingValues, setLightingValues] = useState({
ambient: null,
@@ -5,43 +5,31 @@ import { VolumeRenderingQuality } from './VolumeRenderingQuality';
import { VolumeShift } from './VolumeShift';
import { VolumeLighting } from './VolumeLighting';
import { VolumeShade } from './VolumeShade';
export function VolumeRenderingOptions({
viewportId,
commandsManager,
volumeRenderingQualityRange,
servicesManager,
}: VolumeRenderingOptionsProps): ReactElement {
const [hasShade, setShade] = useState(false);
return (
<AllInOneMenu.ItemPanel>
<VolumeRenderingQuality
viewportId={viewportId}
commandsManager={commandsManager}
servicesManager={servicesManager}
volumeRenderingQualityRange={volumeRenderingQualityRange}
/>
<VolumeShift
viewportId={viewportId}
commandsManager={commandsManager}
servicesManager={servicesManager}
/>
<VolumeShift viewportId={viewportId} />
<div className="mt-2 flex h-8 !h-[20px] w-full flex-shrink-0 items-center justify-start px-2 text-base">
<div className="text-muted-foreground text-sm">Lighting</div>
</div>
<div className="bg-background mt-1 mb-1 h-px w-full"></div>
<div className="hover:bg-accent flex h-8 w-full flex-shrink-0 items-center px-2 text-base hover:rounded">
<VolumeShade
commandsManager={commandsManager}
servicesManager={servicesManager}
viewportId={viewportId}
onClickShade={setShade}
/>
</div>
<VolumeLighting
viewportId={viewportId}
commandsManager={commandsManager}
servicesManager={servicesManager}
hasShade={hasShade}
/>
</AllInOneMenu.ItemPanel>
@@ -3,13 +3,13 @@ import { Icons } from '@ohif/ui-next';
import React, { ReactElement } from 'react';
import { VolumeRenderingPresetsProps } from '../../types/ViewportPresets';
import { VolumeRenderingPresetsContent } from './VolumeRenderingPresetsContent';
import { useSystem } from '@ohif/core';
export function VolumeRenderingPresets({
viewportId,
servicesManager,
commandsManager,
volumeRenderingPresets,
}: VolumeRenderingPresetsProps): ReactElement {
const { servicesManager } = useSystem();
const { uiDialogService } = servicesManager.services;
const onClickPresets = () => {
@@ -21,7 +21,6 @@ export function VolumeRenderingPresets({
contentProps: {
presets: volumeRenderingPresets,
viewportId,
commandsManager,
},
});
};
@@ -1,13 +1,13 @@
import React, { ReactElement, useCallback, useState, useEffect } from 'react';
import { VolumeRenderingQualityProps } from '../../types/ViewportPresets';
import { Numeric } from '@ohif/ui-next';
import { useSystem } from '@ohif/core';
export function VolumeRenderingQuality({
volumeRenderingQualityRange,
commandsManager,
servicesManager,
viewportId,
}: VolumeRenderingQualityProps): ReactElement {
const { servicesManager, commandsManager } = useSystem();
const { cornerstoneViewportService } = servicesManager.services;
const { min, max, step } = volumeRenderingQualityRange;
const [quality, setQuality] = useState(null);
@@ -1,13 +1,13 @@
import React, { ReactElement, useCallback, useEffect, useState } from 'react';
import { Switch } from '@ohif/ui-next';
import { VolumeShadeProps } from '../../types/ViewportPresets';
import { useSystem } from '@ohif/core';
export function VolumeShade({
commandsManager,
viewportId,
servicesManager,
onClickShade = bool => {},
}: VolumeShadeProps): ReactElement {
const { servicesManager, commandsManager } = useSystem();
const { cornerstoneViewportService } = servicesManager.services;
const [shade, setShade] = useState(true);
const [key, setKey] = useState(0);
@@ -1,12 +1,10 @@
import React, { ReactElement, useCallback, useEffect, useState, useRef } from 'react';
import { VolumeShiftProps } from '../../types/ViewportPresets';
import { Numeric } from '@ohif/ui-next';
import { useSystem } from '@ohif/core';
export function VolumeShift({
viewportId,
commandsManager,
servicesManager,
}: VolumeShiftProps): ReactElement {
export function VolumeShift({ viewportId }: VolumeShiftProps): ReactElement {
const { servicesManager, commandsManager } = useSystem();
const { cornerstoneViewportService } = servicesManager.services;
const [minShift, setMinShift] = useState<number | null>(null);
const [maxShift, setMaxShift] = useState<number | null>(null);
@@ -1,20 +1,16 @@
import React, { ReactElement, useCallback } from 'react';
import { AllInOneMenu } from '@ohif/ui-next';
import { WindowLevelPreset } from '../../types/WindowLevel';
import { CommandsManager } from '@ohif/core';
import { useSystem } from '@ohif/core';
import { useTranslation } from 'react-i18next';
export type WindowLevelProps = {
viewportId: string;
presets: Array<Record<string, Array<WindowLevelPreset>>>;
commandsManager: CommandsManager;
};
export function WindowLevel({
viewportId,
commandsManager,
presets,
}: WindowLevelProps): ReactElement {
export function WindowLevel({ viewportId, presets }: WindowLevelProps): ReactElement {
const { commandsManager } = useSystem();
const { t } = useTranslation('WindowLevelActionMenu');
const onSetWindowLevel = useCallback(
@@ -15,8 +15,7 @@ import { VolumeRenderingOptions } from './VolumeRenderingOptions';
import { ViewportPreset } from '../../types/ViewportPresets';
import { VolumeViewport3D } from '@cornerstonejs/core';
import { utilities } from '@cornerstonejs/core';
export const nonWLModalities = ['SR', 'SEG', 'SM', 'RTSTRUCT', 'RTPLAN', 'RTDOSE'];
import { useSystem } from '@ohif/core';
export type WindowLevelActionMenuProps = {
viewportId: string;
@@ -34,13 +33,12 @@ export function WindowLevelActionMenu({
presets,
verticalDirection,
horizontalDirection,
commandsManager,
servicesManager,
colorbarProperties,
displaySets,
volumeRenderingPresets,
volumeRenderingQualityRange,
}: withAppTypes<WindowLevelActionMenuProps>): ReactElement {
const { commandsManager, servicesManager } = useSystem();
const {
colormaps,
colorbarContainerPosition,
@@ -48,6 +46,7 @@ export function WindowLevelActionMenu({
colorbarTickPosition,
width: colorbarWidth,
} = colorbarProperties;
const { colorbarService, cornerstoneViewportService } = servicesManager.services;
const viewportInfo = cornerstoneViewportService.getViewportInfo(viewportId);
const viewport = cornerstoneViewportService.getCornerstoneViewport(viewportId);
@@ -64,7 +63,7 @@ export function WindowLevelActionMenu({
const [is3DVolume, setIs3DVolume] = useState(false);
const onSetColorbar = useCallback(() => {
setViewportColorbar(viewportId, displaySets, commandsManager, servicesManager, {
setViewportColorbar(viewportId, commandsManager, servicesManager, {
colormaps,
ticks: {
position: colorbarTickPosition,
@@ -117,7 +116,6 @@ export function WindowLevelActionMenu({
verticalDirection={verticalDirection}
horizontalDirection={horizontalDirection}
iconClassName={classNames(
// Visible on hover and for the active viewport
activeViewportId === viewportId ? 'visible' : 'invisible group-hover/pane:visible',
'flex shrink-0 cursor-pointer rounded active:text-foreground text-highlight',
isLight ? ' hover:bg-primary/30' : 'hover:bg-primary/30'
@@ -132,9 +130,7 @@ export function WindowLevelActionMenu({
{!is3DVolume && (
<Colorbar
viewportId={viewportId}
displaySets={displaySets.filter(ds => !nonWLModalities.includes(ds.Modality))}
commandsManager={commandsManager}
servicesManager={servicesManager}
displaySets={displaySets.filter(ds => ds.supportsWindowLevel === true)}
colorbarProperties={colorbarProperties}
/>
)}
@@ -150,9 +146,7 @@ export function WindowLevelActionMenu({
className="flex h-full w-full flex-col"
colormaps={colormaps}
viewportId={viewportId}
displaySets={displaySets.filter(ds => !nonWLModalities.includes(ds.Modality))}
commandsManager={commandsManager}
servicesManager={servicesManager}
displaySets={displaySets.filter(ds => ds.supportsWindowLevel === true)}
/>
</AllInOneMenu.SubMenu>
)}
@@ -165,7 +159,6 @@ export function WindowLevelActionMenu({
>
<WindowLevel
viewportId={viewportId}
commandsManager={commandsManager}
presets={presets}
/>
</AllInOneMenu.SubMenu>
@@ -173,9 +166,7 @@ export function WindowLevelActionMenu({
{volumeRenderingPresets && is3DVolume && (
<VolumeRenderingPresets
servicesManager={servicesManager}
viewportId={viewportId}
commandsManager={commandsManager}
volumeRenderingPresets={volumeRenderingPresets}
/>
)}
@@ -184,9 +175,7 @@ export function WindowLevelActionMenu({
<AllInOneMenu.SubMenu itemLabel="Rendering Options">
<VolumeRenderingOptions
viewportId={viewportId}
commandsManager={commandsManager}
volumeRenderingQualityRange={volumeRenderingQualityRange}
servicesManager={servicesManager}
/>
</AllInOneMenu.SubMenu>
)}
@@ -1,12 +1,12 @@
import React, { ReactNode } from 'react';
import { nonWLModalities } from './WindowLevelActionMenu';
import { WindowLevelActionMenuProps } from './WindowLevelActionMenu';
import { useSystem } from '@ohif/core';
import { WindowLevelActionMenu as WindowLevelActionMenuComponent } from './WindowLevelActionMenu';
export function getWindowLevelActionMenu({
function WindowLevelActionMenu({
viewportId,
element,
displaySets,
servicesManager,
commandsManager,
verticalDirection,
horizontalDirection,
}: withAppTypes<{
@@ -14,31 +14,25 @@ export function getWindowLevelActionMenu({
element: HTMLElement;
displaySets: AppTypes.DisplaySet[];
}>): ReactNode {
const { servicesManager } = useSystem();
const { customizationService } = servicesManager.services;
const presets = customizationService.getCustomization('cornerstone.windowLevelPresets');
const colorbarProperties = customizationService.getCustomization('cornerstone.colorbar');
const { volumeRenderingPresets, volumeRenderingQualityRange } =
customizationService.getCustomization('cornerstone.3dVolumeRendering');
const WindowLevelActionMenu = customizationService.getCustomization(
'viewportActionMenu.windowLevelActionMenu'
);
const displaySetPresets = displaySets
.filter(displaySet => presets[displaySet.Modality])
.map(displaySet => {
return { [displaySet.Modality]: presets[displaySet.Modality] };
});
const modalities = displaySets
.map(displaySet => displaySet.Modality)
.filter(modality => !nonWLModalities.includes(modality));
const modalities = displaySets.map(displaySet => displaySet.supportsWindowLevel);
if (modalities.length === 0) {
return null;
}
const WindowLevelActionMenuComponent = WindowLevelActionMenu?.component;
return (
<WindowLevelActionMenuComponent
viewportId={viewportId}
@@ -46,8 +40,6 @@ export function getWindowLevelActionMenu({
presets={displaySetPresets}
verticalDirection={verticalDirection}
horizontalDirection={horizontalDirection}
commandsManager={commandsManager}
servicesManager={servicesManager}
colorbarProperties={colorbarProperties}
displaySets={displaySets}
volumeRenderingPresets={volumeRenderingPresets}
@@ -55,3 +47,7 @@ export function getWindowLevelActionMenu({
/>
);
}
export function getWindowLevelActionMenu(props: WindowLevelActionMenuProps) {
return <WindowLevelActionMenu {...props} />;
}
@@ -0,0 +1,3 @@
// Volume loader schemes
export const VOLUME_LOADER_SCHEME = 'cornerstoneStreamingImageVolume';
export const DYNAMIC_VOLUME_LOADER_SCHEME = 'cornerstoneStreamingDynamicImageVolume';
@@ -0,0 +1,23 @@
export default {
'cornerstone.modalityOverlayDefaultColorMaps': {
defaultSettings: {
PT: {
colormap: 'hsv',
// Note: Right now, there is a nonlinear relationship between the opacity value
// below and how it will get applied to the image. The limitation is in rendering.
// We are working on this and will remove this note when it's fixed.
// But don't expect 0.5 to be 50% opacity, but rather close to that.
opacity: 0.5,
},
RTDOSE: {
colormap: 'Isodose',
// Note: Right now, there is a nonlinear relationship between the opacity value
// below and how it will get applied to the image. The limitation is in rendering.
// We are working on this and will remove this note when it's fixed.
// But don't expect 0.5 to be 50% opacity, but rather close to that.
opacity: 0.5,
},
},
},
};
@@ -1,14 +1,55 @@
import viewportActionCornersService from '../services/ViewportActionCornersService/ViewportActionCornersService';
import { WindowLevelActionMenu } from '../components/WindowLevelActionMenu/WindowLevelActionMenu';
import { getWindowLevelActionMenu } from '../components/WindowLevelActionMenu/getWindowLevelActionMenu';
import { getViewportDataOverlaySettingsMenu } from '../components/ViewportDataOverlaySettingMenu';
import { getViewportOrientationMenu } from '../components/ViewportOrientationMenu';
import { AllInOneMenu } from '@ohif/ui-next';
// Generate component renderer functions for each component type
const createOrientationMenu = ({ viewportId, element, location }) => {
return getViewportOrientationMenu({
viewportId,
element,
location,
});
};
const createDataOverlay = ({ viewportId, element, displaySets, location }) => {
return getViewportDataOverlaySettingsMenu({
viewportId,
element,
displaySets,
location,
});
};
const createWindowLevelMenu = ({ viewportId, element, displaySets }) => {
return getWindowLevelActionMenu({
viewportId,
element,
displaySets,
verticalDirection: AllInOneMenu.VerticalDirection.TopToBottom,
horizontalDirection: AllInOneMenu.HorizontalDirection.LeftToRight,
});
};
export default {
'viewportActionMenu.windowLevelActionMenu': {
enabled: true,
location: viewportActionCornersService.LOCATIONS.topRight,
component: WindowLevelActionMenu,
},
'viewportActionMenu.segmentationOverlay': {
enabled: true,
location: viewportActionCornersService.LOCATIONS.topRight,
},
'viewportActionMenu.topLeft': [
{
id: 'orientationMenu',
enabled: true,
component: createOrientationMenu,
},
{
id: 'dataOverlay',
enabled: true,
component: createDataOverlay,
},
{
id: 'windowLevelActionMenu',
enabled: true,
component: createWindowLevelMenu,
},
],
'viewportActionMenu.topRight': [],
'viewportActionMenu.bottomLeft': [],
'viewportActionMenu.bottomRight': [],
};
@@ -6,6 +6,7 @@ import viewportClickCommandsCustomization from './customizations/viewportClickCo
import measurementsCustomization from './customizations/measurementsCustomization';
import volumeRenderingCustomization from './customizations/volumeRenderingCustomization';
import colorbarCustomization from './customizations/colorbarCustomization';
import modalityColorMapCustomization from './customizations/modalityColorMapCustomization';
import windowLevelPresetsCustomization from './customizations/windowLevelPresetsCustomization';
import miscCustomization from './customizations/miscCustomization';
import captureViewportModalCustomization from './customizations/captureViewportModalCustomization';
@@ -25,6 +26,7 @@ function getCustomizationModule({ commandsManager, servicesManager }) {
...measurementsCustomization,
...volumeRenderingCustomization,
...colorbarCustomization,
...modalityColorMapCustomization,
...windowLevelPresetsCustomization,
...miscCustomization,
...captureViewportModalCustomization,
@@ -1,4 +1,5 @@
import OHIF from '@ohif/core';
import i18n from '@ohif/i18n';
import { utilities as csUtils, Enums as csEnums } from '@cornerstonejs/core';
import dcmjs from 'dcmjs';
import { dicomWebUtils } from '@ohif/extension-default';
@@ -112,6 +113,7 @@ function _getDisplaySetsFromSeries(instances, servicesManager, extensionManager)
instances,
othersFrameOfReferenceUID,
imageIds: instances.map(instance => instance.imageId),
label: SeriesDescription || `${i18n.t('Series')} ${SeriesNumber} - ${i18n.t('SM')}`,
};
// The microscopy viewer directly accesses the metadata already loaded, and
// uses the DICOMweb client library directly for loading, so it has to be
@@ -233,7 +233,7 @@ function _evaluateToggle({
}
const toolName = toolbarService.getToolNameForButton(button);
if (!toolGroup.hasTool(toolName)) {
if (!toolGroup?.hasTool(toolName)) {
return getDisabledState(disabledText);
}
@@ -0,0 +1,5 @@
export { default as useViewportActionCorners } from './useViewportActionCorners';
export * from './useViewportActionCorners';
export * from './useActiveViewportSegmentationRepresentations';
export * from './useMeasurements';
export * from './useSegmentations';
@@ -84,7 +84,7 @@ function mapSegmentationToDisplay(segmentation, customizationService) {
* @param options.debounceTime - Debounce time in milliseconds for updates.
* @returns An array of segmentation data.
*/
export function useSegmentations(options: {
export function useSegmentations(options?: {
subscribeToDataModified?: boolean;
debounceTime?: number;
}): SegmentationData[] {
@@ -0,0 +1,113 @@
import { useEffect } from 'react';
import React from 'react';
import { ViewportActionCornersLocations } from '@ohif/ui-next';
import { useSystem } from '@ohif/core/src';
/**
* Viewport action corner location type
*/
export interface ViewportActionCornerLocation {
type: 'VIEWPORT';
id: string;
corner?: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';
}
/**
* Viewport action corner service interface
*/
export interface ViewportActionCornerService {
addComponent: (params: {
viewportId: string;
id: string;
component: React.ReactNode;
location: ViewportActionCornersLocations | string;
indexPriority?: number;
}) => void;
clear: (viewportId: string) => void;
getAlignAndSide: (location: ViewportActionCornersLocations | string) => {
align: 'start' | 'center' | 'end';
side: 'top' | 'right' | 'bottom' | 'left';
};
}
/**
* Customization service interface
*/
export interface CustomizationService {
getCustomization: (name: string) => any;
}
/**
* Props for the useViewportActionCorners hook
*/
interface UseViewportActionCornersProps {
viewportId: string;
elementRef: React.MutableRefObject<HTMLDivElement>;
displaySets: AppTypes.DisplaySet[];
}
// Map of customization keys to their corresponding enum values
const locationMap = {
'viewportActionMenu.topLeft': ViewportActionCornersLocations.topLeft,
'viewportActionMenu.topRight': ViewportActionCornersLocations.topRight,
'viewportActionMenu.bottomLeft': ViewportActionCornersLocations.bottomLeft,
'viewportActionMenu.bottomRight': ViewportActionCornersLocations.bottomRight,
};
/**
* Hook to manage viewport action corners for a Cornerstone viewport
*/
export function useViewportActionCorners({
viewportId,
elementRef,
displaySets,
}: UseViewportActionCornersProps): void {
const { servicesManager } = useSystem();
const { viewportActionCornersService, customizationService } = servicesManager.services;
useEffect(() => {
// Process each location
Object.entries(locationMap).forEach(([locationKey, locationValue]) => {
const items = customizationService.getCustomization(locationKey);
if (!items || !items.length) {
return;
}
items.forEach(item => {
if (!item.enabled) {
return;
}
const componentId = item.id;
if (item.component) {
// Use the component renderer provided directly in the item
const component = item.component({
viewportId,
element: elementRef.current,
displaySets,
location: locationValue,
});
viewportActionCornersService.addComponent({
viewportId,
id: componentId,
component,
location: locationValue,
});
} else if (item.component) {
// Handle static components
viewportActionCornersService.addComponent({
viewportId,
id: item.id,
component: item.component,
location: locationValue,
});
}
});
});
}, [displaySets, viewportId, viewportActionCornersService, elementRef]);
}
export default useViewportActionCorners;
@@ -0,0 +1,247 @@
import { useMemo } from 'react';
import { useSystem, utils } from '@ohif/core';
import { useViewportGrid } from '@ohif/ui-next';
import {
getEnhancedDisplaySets,
sortByOverlayable,
DERIVED_OVERLAY_MODALITIES,
} from '../components/ViewportDataOverlaySettingMenu/utils';
const sortByPriority = (a, b) => {
if (utils.isLowPriorityModality(a.Modality)) {
return 1;
}
return -1;
};
/**
* Options for the useViewportDisplaySets hook
*/
export type UseViewportDisplaySetsOptions = {
/**
* Whether to include background display set
*/
includeBackground?: boolean;
/**
* Whether to include foreground display sets
*/
includeForeground?: boolean;
/**
* Whether to include overlay display sets
*/
includeOverlay?: boolean;
/**
* Whether to include potential overlay display sets
*/
includePotentialOverlay?: boolean;
/**
* Whether to include potential foreground display sets
*/
includePotentialForeground?: boolean;
/**
* Whether to include potential background display sets
*/
includePotentialBackground?: boolean;
};
/**
* Return type for useViewportDisplaySets
*/
export type ViewportDisplaySets = {
/**
* The primary display set for the viewport (base image)
*/
backgroundDisplaySet?: any;
/**
* Display sets currently shown with background (non-overlay layers)
*/
foregroundDisplaySets?: any[];
/**
* Segmentation display sets currently applied as overlays
*/
overlayDisplaySets?: any[];
/**
* Display sets that could be toggled on as overlays (derived modalities)
*/
potentialOverlayDisplaySets?: any[];
/**
* Display sets that could be added as foreground layers
*/
potentialForegroundDisplaySets?: any[];
/**
* Display sets that could replace the current background
*/
potentialBackgroundDisplaySets?: any[];
};
/**
* Hook to provide display sets and overlay information for a viewport based on options.
*
* @param viewportId - The viewport ID to get display sets for
* @param options - Options to control which display sets to compute
* @returns Object containing requested display set collections based on options
*/
export function useViewportDisplaySets(
viewportId: string,
options?: UseViewportDisplaySetsOptions
): ViewportDisplaySets {
const { servicesManager } = useSystem();
const { displaySetService, segmentationService } = servicesManager.services;
// Note: this is very important we should use the useViewportGrid hook here,
// since if the viewport displaySet is changed we should re-run this hook
// to get the latest displaySets
const [_, viewportGridService] = useViewportGrid();
// Apply defaults - include everything if no options specified
const {
includeBackground = true,
includeForeground = true,
includeOverlay = true,
includePotentialOverlay = true,
includePotentialForeground = true,
includePotentialBackground = true,
} = options || {};
// Get all available display sets (only if needed)
const needsAllDisplaySets = includePotentialBackground;
const allDisplaySets = useMemo(
() => (needsAllDisplaySets ? displaySetService.getActiveDisplaySets() : []),
[displaySetService, needsAllDisplaySets]
);
// Get all available segmentations (only if needed)
const needsSegmentations = includeOverlay;
const segmentationRepresentations = useMemo(
() =>
needsSegmentations ? segmentationService.getSegmentationRepresentations(viewportId) : [],
[segmentationService, viewportId, needsSegmentations]
);
const overlayDisplaySets = useMemo(() => {
if (!includeOverlay) {
return [];
}
return segmentationRepresentations.map(repr => {
const displaySet = displaySetService.getDisplaySetByUID(repr.segmentationId);
return displaySet;
});
}, [includeOverlay, segmentationRepresentations, displaySetService]);
const overlayDisplaySetUIDs = useMemo(
() => overlayDisplaySets.map(ds => ds.displaySetInstanceUID),
[overlayDisplaySets]
);
// Get enhanced display sets (only if needed)
const needsEnhancedDisplaySets =
includeBackground || includeForeground || includePotentialOverlay || includePotentialForeground;
const { viewportDisplaySets, enhancedDisplaySets } = useMemo(() => {
if (!needsEnhancedDisplaySets) {
return { viewportDisplaySets: [], enhancedDisplaySets: [] };
}
return getEnhancedDisplaySets({
viewportId,
services: { displaySetService, viewportGridService },
});
}, [viewportId, displaySetService, viewportGridService, needsEnhancedDisplaySets]);
const backgroundDisplaySet = useMemo(
() => (includeBackground ? viewportDisplaySets[0] : undefined),
[includeBackground, viewportDisplaySets]
);
const foregroundDisplaySets = useMemo(() => {
if (!includeForeground || !backgroundDisplaySet) {
return [];
}
return viewportDisplaySets.filter(
ds =>
!DERIVED_OVERLAY_MODALITIES.includes(ds.Modality) &&
ds.displaySetInstanceUID !== backgroundDisplaySet.displaySetInstanceUID
);
}, [includeForeground, viewportDisplaySets, backgroundDisplaySet]);
const foregroundDisplaySetUIDs = useMemo(
() => foregroundDisplaySets.map(ds => ds.displaySetInstanceUID),
[foregroundDisplaySets]
);
const potentialOverlayDisplaySets = useMemo(() => {
if (!includePotentialOverlay) {
return [];
}
return enhancedDisplaySets
.filter(
ds =>
DERIVED_OVERLAY_MODALITIES.includes(ds.Modality) &&
!overlayDisplaySetUIDs.includes(ds.displaySetInstanceUID) &&
ds.isOverlayable
)
.sort(sortByOverlayable);
}, [includePotentialOverlay, enhancedDisplaySets, overlayDisplaySetUIDs]);
const potentialForegroundDisplaySets = useMemo(() => {
if (!includePotentialForeground) {
return [];
}
return enhancedDisplaySets
.filter(
ds =>
!DERIVED_OVERLAY_MODALITIES.includes(ds.Modality) &&
!foregroundDisplaySetUIDs.includes(ds.displaySetInstanceUID) &&
ds.isOverlayable
)
.sort(sortByPriority);
}, [includePotentialForeground, enhancedDisplaySets, foregroundDisplaySetUIDs]);
const potentialBackgroundDisplaySets = useMemo(() => {
if (!includePotentialBackground || !backgroundDisplaySet) {
return [];
}
return allDisplaySets
.filter(
ds =>
!DERIVED_OVERLAY_MODALITIES.includes(ds.Modality) &&
ds.displaySetInstanceUID !== backgroundDisplaySet.displaySetInstanceUID &&
!overlayDisplaySetUIDs.includes(ds.displaySetInstanceUID) &&
!foregroundDisplaySetUIDs.includes(ds.displaySetInstanceUID)
)
.sort(sortByPriority);
}, [
includePotentialBackground,
allDisplaySets,
backgroundDisplaySet,
overlayDisplaySetUIDs,
foregroundDisplaySetUIDs,
]);
const result: ViewportDisplaySets = {};
if (includeBackground) {
result.backgroundDisplaySet = backgroundDisplaySet;
}
if (includeForeground) {
result.foregroundDisplaySets = foregroundDisplaySets;
}
if (includeOverlay) {
result.overlayDisplaySets = overlayDisplaySets;
}
if (includePotentialOverlay) {
result.potentialOverlayDisplaySets = potentialOverlayDisplaySets;
}
if (includePotentialForeground) {
result.potentialForegroundDisplaySets = potentialForegroundDisplaySets;
}
if (includePotentialBackground) {
result.potentialBackgroundDisplaySets = potentialBackgroundDisplaySets;
}
return result;
}
+3
View File
@@ -269,4 +269,7 @@ export {
CornerstoneViewportDownloadForm,
utils,
};
// Export constants
export { VOLUME_LOADER_SCHEME, DYNAMIC_VOLUME_LOADER_SCHEME } from './constants';
export default cornerstoneExtension;
+121 -39
View File
@@ -96,6 +96,8 @@ export default async function init({
viewportGridService,
segmentationService,
measurementService,
colorbarService,
displaySetService,
} = servicesManager.services;
window.services = servicesManager.services;
@@ -232,16 +234,6 @@ export default async function init({
}
);
// resize the cornerstone viewport service when the grid size changes
// IMPORTANT: this should happen outside of the OHIFCornerstoneViewport
// since it will trigger a rerender of each viewport and each resizing
// the offscreen canvas which would result in a performance hit, this should
// done only once per grid resize here. Doing it once here, allows us to reduce
// the refreshRage(in ms) to 10 from 50. I tried with even 1 or 5 ms it worked fine
viewportGridService.subscribe(viewportGridService.EVENTS.GRID_SIZE_CHANGED, () => {
cornerstoneViewportService.resize(true);
});
initContextMenu({
cornerstoneViewportService,
customizationService,
@@ -265,8 +257,17 @@ export default async function init({
eventTarget.addEventListener(EVENTS.IMAGE_LOAD_FAILED, imageLoadFailedHandler);
eventTarget.addEventListener(EVENTS.IMAGE_LOAD_ERROR, imageLoadFailedHandler);
const getDisplaySetFromVolumeId = (volumeId: string) => {
const allDisplaySets = displaySetService.getActiveDisplaySets();
const volume = cornerstone.cache.getVolume(volumeId);
const imageIds = volume.imageIds;
return allDisplaySets.find(ds => ds.imageIds?.some(id => imageIds.includes(id)));
};
function elementEnabledHandler(evt) {
const { element } = evt.detail;
const { viewport } = getEnabledElement(element);
initViewTiming({ element });
element.addEventListener(EVENTS.CAMERA_RESET, evt => {
const { element } = evt.detail;
@@ -278,7 +279,10 @@ export default async function init({
commandsManager.runCommand('resetCrosshairs', { viewportId });
});
initViewTiming({ element });
// limitation: currently supporting only volume viewports with fusion
if (viewport.type !== cornerstone.Enums.ViewportType.ORTHOGRAPHIC) {
return;
}
}
eventTarget.addEventListener(EVENTS.ELEMENT_ENABLED, elementEnabledHandler.bind(null));
@@ -289,58 +293,136 @@ export default async function init({
eventTarget.addEventListenerDebounced(
EVENTS.ERROR_EVENT,
({ detail }) => {
// Create a stable ID for deduplication based on error type and message
const errorId = `cornerstone-error-${detail.type}-${detail.message.substring(0, 50)}`;
uiNotificationService.show({
title: detail.type,
message: detail.message,
type: 'error',
id: errorId,
allowDuplicates: false, // Prevent duplicate error notifications
deduplicationInterval: 30000, // 30 seconds deduplication window
});
},
100
);
// Subscribe to actor events to dynamically update colorbars
// Call this function when initializing
initializeWebWorkerProgressHandler(servicesManager.services.uiNotificationService);
const { unsubscribe: unsubscribeSegmentationCreated } = segmentationService.subscribe(
segmentationService.EVENTS.SEGMENTATION_ADDED,
evt => {
const { segmentationId } = evt;
const displaySet = displaySetService.getDisplaySetByUID(segmentationId);
if (displaySet) {
return;
}
const segmentation = segmentationService.getSegmentation(segmentationId);
const label = segmentation.cachedStats.info;
const imageIds = segmentation.representationData.Labelmap.imageIds;
// Create a display set for the segmentation
const segmentationDisplaySet = {
displaySetInstanceUID: segmentationId,
SOPClassUID: '1.2.840.10008.5.1.4.1.1.66.4',
SOPClassHandlerId: '@ohif/extension-cornerstone-dicom-seg.sopClassHandlerModule.dicom-seg',
SeriesDescription: label,
Modality: 'SEG',
numImageFrames: imageIds.length,
imageIds,
isOverlayDisplaySet: true,
label,
madeInClient: true,
segmentationId: segmentationId,
isDerived: true,
};
displaySetService.addDisplaySets(segmentationDisplaySet);
}
);
const unsubscriptions = [
unsubscribeSegmentationDataModifiedHandler,
unsubscribeSegmentationModifiedHandler,
unsubscribeSegmentationCreated,
];
return { unsubscriptions };
}
function initializeWebWorkerProgressHandler(uiNotificationService) {
const activeToasts = new Map();
// Use a single map to track all active worker tasks
const activeWorkerTasks = new Map();
// eventTarget.addEventListener(EVENTS.WEB_WORKER_PROGRESS, ({ detail }) => {
// const { progress, type, id } = detail;
// Create a normalized task key that doesn't include the random ID
// This helps us identify and deduplicate the same type of task
const getNormalizedTaskKey = type => {
return `worker-task-${type.toLowerCase().replace(/\s+/g, '-')}`;
};
// const cacheKey = `${type}-${id}`;
// if (progress === 0 && !activeToasts.has(cacheKey)) {
// const progressPromise = new Promise((resolve, reject) => {
// activeToasts.set(cacheKey, { resolve, reject });
// });
eventTarget.addEventListener(EVENTS.WEB_WORKER_PROGRESS, ({ detail }) => {
const { progress, type, id } = detail;
// uiNotificationService.show({
// id: cacheKey,
// title: `${type}`,
// message: `${type}: ${progress}%`,
// autoClose: false,
// promise: progressPromise,
// promiseMessages: {
// loading: `Computing...`,
// success: `Completed successfully`,
// error: 'Web Worker failed',
// },
// });
// } else {
// if (progress === 100) {
// const { resolve } = activeToasts.get(cacheKey);
// resolve({ progress, type });
// activeToasts.delete(cacheKey);
// }
// }
// });
// Skip notifications for compute statistics
if (type === cornerstoneTools.Enums.WorkerTypes.COMPUTE_STATISTICS) {
return;
}
const normalizedKey = getNormalizedTaskKey(type);
if (progress === 0) {
// Check if we're already tracking a task of this type
if (!activeWorkerTasks.has(normalizedKey)) {
const progressPromise = new Promise((resolve, reject) => {
activeWorkerTasks.set(normalizedKey, {
resolve,
reject,
originalId: id,
type,
});
});
uiNotificationService.show({
id: normalizedKey, // Use the normalized key as ID for better deduplication
title: `${type}`,
message: `Computing...`,
autoClose: false,
allowDuplicates: false,
deduplicationInterval: 60000, // 60 seconds - prevent frequent notifications of same type
promise: progressPromise,
promiseMessages: {
loading: `Computing...`,
success: `Completed successfully`,
error: 'Web Worker failed',
},
});
} else {
// Already tracking this type of task, just let it continue
console.debug(`Already tracking a "${type}" task, skipping duplicate notification`);
}
}
// Task completed
else if (progress === 100) {
// Check if we have this task type in our tracking map
const taskData = activeWorkerTasks.get(normalizedKey);
if (taskData) {
// Resolve the promise to update the notification
const { resolve } = taskData;
resolve({ progress, type });
// Remove from tracking
activeWorkerTasks.delete(normalizedKey);
console.debug(`Worker task "${type}" completed successfully`);
}
}
});
}
/**
@@ -48,6 +48,8 @@ import ImageOverlayViewerTool from './tools/ImageOverlayViewerTool';
export default function initCornerstoneTools(configuration = {}) {
CrosshairsTool.isAnnotation = false;
LabelmapSlicePropagationTool.isAnnotation = false;
MarkerLabelmapTool.isAnnotation = false;
ReferenceLinesTool.isAnnotation = false;
AdvancedMagnifyTool.isAnnotation = false;
PlanarFreehandContourSegmentationTool.isAnnotation = false;
@@ -38,14 +38,16 @@ export default class ColorbarService extends PubSubService {
public static REGISTRATION = {
name: 'colorbarService',
create: () => {
return new ColorbarService();
create: ({ servicesManager }: OhifTypes.Extensions.ExtensionParams) => {
return new ColorbarService(servicesManager);
},
};
colorbars = {};
servicesManager: AppTypes.ServicesManager;
constructor() {
constructor(servicesManager: AppTypes.ServicesManager) {
super(ColorbarService.EVENTS);
this.servicesManager = servicesManager;
}
/**
@@ -68,6 +70,7 @@ export default class ColorbarService extends PubSubService {
* @param options Configuration options for the colorbar, including position, colormaps, active colormap name, ticks, and width.
*/
public addColorbar(viewportId, displaySetInstanceUIDs, options = {} as ColorbarOptions) {
const { displaySetService } = this.servicesManager.services;
const renderingEngine = getRenderingEngine(RENDERING_ENGINE_ID);
const viewport = renderingEngine.getViewport(viewportId);
@@ -95,6 +98,12 @@ export default class ColorbarService extends PubSubService {
);
displaySetInstanceUIDs.forEach((displaySetInstanceUID, index) => {
// don't show colorbar for overlay display sets (e.g. segmentation)
const displaySet = displaySetService.getDisplaySetByUID(displaySetInstanceUID);
if (displaySet.isOverlayDisplaySet) {
return;
}
const volumeId = this.getVolumeIdForIdentifier(viewport, displaySetInstanceUID);
const properties = viewport?.getProperties(volumeId);
const colormap = properties?.colormap;
@@ -123,9 +132,19 @@ export default class ColorbarService extends PubSubService {
volumeId: viewport instanceof VolumeViewport ? volumeId : undefined,
});
if (this.colorbars[viewportId]) {
this.colorbars[viewportId].push({ colorbar, container: colorbarContainer });
this.colorbars[viewportId].push({
colorbar,
container: colorbarContainer,
displaySetInstanceUID,
});
} else {
this.colorbars[viewportId] = [{ colorbar, container: colorbarContainer }];
this.colorbars[viewportId] = [
{
colorbar,
container: colorbarContainer,
displaySetInstanceUID,
},
];
}
});
@@ -136,24 +155,50 @@ export default class ColorbarService extends PubSubService {
}
/**
* Removes the colorbar associated with a given viewport ID. This involves cleaning up any created DOM elements and internal references.
* Removes a colorbar from a specific viewport. If displaySetInstanceUID is provided,
* only the colorbar associated with that specific displaySetInstanceUID will be removed.
* Otherwise, all colorbars for the given viewport will be removed.
*
* @param viewportId The identifier for the viewport from which the colorbar will be removed.
* @param displaySetInstanceUID Optional. The specific display set instance UID associated with the colorbar to remove.
*/
public removeColorbar(viewportId) {
public removeColorbar(viewportId, displaySetInstanceUID?: string) {
const colorbarInfo = this.colorbars[viewportId];
if (!colorbarInfo) {
return;
}
colorbarInfo.forEach(({ colorbar, container }) => {
container.parentNode.removeChild(container);
});
if (displaySetInstanceUID) {
// Find the index of the colorbar with the matching displaySetInstanceUID
const index = colorbarInfo.findIndex(
info => info.displaySetInstanceUID === displaySetInstanceUID
);
delete this.colorbars[viewportId];
if (index !== -1) {
// Remove only the specific colorbar container
const { container } = colorbarInfo[index];
container.parentNode.removeChild(container);
// Remove the colorbar from the array
colorbarInfo.splice(index, 1);
// If there are no more colorbars for this viewport, remove the entry
if (colorbarInfo.length === 0) {
delete this.colorbars[viewportId];
}
}
} else {
// Remove all colorbars for the viewport (original behavior)
colorbarInfo.forEach(({ container }) => {
container.parentNode.removeChild(container);
});
delete this.colorbars[viewportId];
}
this._broadcastEvent(ColorbarService.EVENTS.STATE_CHANGED, {
viewportId,
displaySetInstanceUID,
changeType: ChangeTypes.Removed,
});
}
@@ -3,8 +3,7 @@ import { cache as cs3DCache, Enums, volumeLoader } from '@cornerstonejs/core';
import getCornerstoneViewportType from '../../utils/getCornerstoneViewportType';
import { StackViewportData, VolumeViewportData } from '../../types/CornerstoneCacheService';
const VOLUME_LOADER_SCHEME = 'cornerstoneStreamingImageVolume';
import { VOLUME_LOADER_SCHEME } from '../../constants';
class CornerstoneCacheService {
static REGISTRATION = {
@@ -8,26 +8,29 @@
* and geometry Id
*/
export function mapROIContoursToRTStructData(structureSet: unknown, rtDisplaySetUID: unknown) {
return structureSet.ROIContours.map(({ contourPoints, ROINumber, ROIName, colorArray }) => {
const data = contourPoints.map(({ points, ...rest }) => {
const newPoints = points.map(({ x, y, z }) => {
return [x, y, z];
return structureSet.ROIContours.map(
({ contourPoints, ROINumber, ROIName, colorArray, ROIGroup }) => {
const data = contourPoints.map(({ points, ...rest }) => {
const newPoints = points.map(({ x, y, z }) => {
return [x, y, z];
});
return {
...rest,
points: newPoints,
};
});
const id = ROIName || ROINumber;
return {
...rest,
points: newPoints,
data,
id,
segmentIndex: ROINumber,
color: colorArray,
group: ROIGroup,
geometryId: `${rtDisplaySetUID}:${id}:segmentIndex-${ROINumber}`,
};
});
const id = ROIName || ROINumber;
return {
data,
id,
segmentIndex: ROINumber,
color: colorArray,
geometryId: `${rtDisplaySetUID}:${id}:segmentIndex-${ROINumber}`,
};
});
}
);
}
@@ -29,6 +29,7 @@ import { updateLabelmapSegmentationImageReferences } from '@cornerstonejs/tools/
import { triggerSegmentationRepresentationModified } from '@cornerstonejs/tools/segmentation/triggerSegmentationEvents';
import { convertStackToVolumeLabelmap } from '@cornerstonejs/tools/segmentation/helpers/convertStackToVolumeLabelmap';
import { getLabelmapImageIds } from '@cornerstonejs/tools/segmentation';
import { VOLUME_LOADER_SCHEME } from '../../constants';
const LABELMAP = csToolsEnums.SegmentationRepresentations.Labelmap;
const CONTOUR = csToolsEnums.SegmentationRepresentations.Contour;
@@ -80,8 +81,6 @@ const EVENTS = {
const VALUE_TYPES = {};
const VOLUME_LOADER_SCHEME = 'cornerstoneStreamingImageVolume';
class SegmentationService extends PubSubService {
static REGISTRATION = {
name: 'segmentationService',
@@ -615,7 +614,7 @@ class SegmentationService extends PubSubService {
// Process each segment similarly to the SEG function
for (const rtStructData of allRTStructData) {
const { data, id, color, segmentIndex, geometryId } = rtStructData;
const { data, id, color, segmentIndex, geometryId, group } = rtStructData;
// Add the color to the colorLUT array
colorLUT.push(color);
@@ -646,6 +645,7 @@ class SegmentationService extends PubSubService {
cachedStats: segmentsCachedStats,
locked: false,
active: false,
group,
};
// Broadcast segment loading progress
@@ -10,6 +10,11 @@ export type ActionComponentInfo = {
indexPriority?: number;
};
export type AlignAndSide = {
align: 'start' | 'end' | 'center';
side: 'top' | 'bottom' | 'left' | 'right';
};
class ViewportActionCornersService extends PubSubService {
public static readonly EVENTS = {};
public static readonly LOCATIONS = ViewportActionCornersLocations;
@@ -66,6 +71,22 @@ class ViewportActionCornersService extends PubSubService {
public clear(viewportId: string) {
this.serviceImplementation._clear(viewportId);
}
public getAlignAndSide(location: ViewportActionCornersLocations): AlignAndSide {
switch (location) {
case ViewportActionCornersLocations.topLeft:
return { align: 'start', side: 'bottom' };
case ViewportActionCornersLocations.topRight:
return { align: 'end', side: 'bottom' };
case ViewportActionCornersLocations.bottomLeft:
return { align: 'start', side: 'top' };
case ViewportActionCornersLocations.bottomRight:
return { align: 'end', side: 'top' };
default:
console.debug('Unknown location, defaulting to bottom-start');
return { align: 'start', side: 'bottom' };
}
}
}
export default ViewportActionCornersService;
@@ -32,12 +32,16 @@ import { useLutPresentationStore } from '../../stores/useLutPresentationStore';
import { usePositionPresentationStore } from '../../stores/usePositionPresentationStore';
import { useSynchronizersStore } from '../../stores/useSynchronizersStore';
import { useSegmentationPresentationStore } from '../../stores/useSegmentationPresentationStore';
import { VOLUME_LOADER_SCHEME } from '../../constants';
const EVENTS = {
VIEWPORT_DATA_CHANGED: 'event::cornerstoneViewportService:viewportDataChanged',
VIEWPORT_VOLUMES_CHANGED: 'event::cornerstoneViewportService:viewportVolumesChanged',
};
const MIN_STACK_VIEWPORTS_TO_ENQUEUE_RESIZE = 12;
const MIN_VOLUME_VIEWPORTS_TO_ENQUEUE_RESIZE = 6;
export const WITH_NAVIGATION = { withNavigation: true, withOrientation: false };
/**
@@ -62,9 +66,6 @@ class CornerstoneViewportService extends PubSubService implements IViewportServi
beforeResizePositionPresentations: Map<string, PositionPresentation> = new Map();
// Some configs
enableResizeDetector: true;
resizeRefreshRateMs: 200;
resizeRefreshMode: 'debounce';
servicesManager: AppTypes.ServicesManager = null;
resizeQueue = [];
@@ -125,13 +126,8 @@ class CornerstoneViewportService extends PubSubService implements IViewportServi
/**
* It triggers the resize on the rendering engine, and renders the viewports
*
* @param isGridResize - if the resize is triggered by a grid resize
* this is used to avoid double resize of the viewports since if the
* grid is resized, all viewports will be resized so there is no need
* to resize them individually which will get triggered by their
* individual resize observers
*/
public resize(isGridResize = false) {
public resize() {
// https://stackoverflow.com/a/26279685
// This resize() call, among other things, rerenders the viewports. But when the entire viewer is
// display: none'd, it makes the size of all hidden elements 0, including the viewport canvas and its containers.
@@ -147,12 +143,24 @@ class CornerstoneViewportService extends PubSubService implements IViewportServi
return;
}
const numStackViewportsInViewportGrid = Array.from(this.viewportsById.values()).filter(
viewportInfo => viewportInfo.getViewportType() === csEnums.ViewportType.STACK
).length;
const numVolumeViewportsInViewportGrid = Array.from(this.viewportsById.values()).filter(
viewportInfo => viewportInfo.getViewportType() === csEnums.ViewportType.ORTHOGRAPHIC
).length;
const isEasyResize =
numStackViewportsInViewportGrid <= MIN_STACK_VIEWPORTS_TO_ENQUEUE_RESIZE &&
numVolumeViewportsInViewportGrid <= MIN_VOLUME_VIEWPORTS_TO_ENQUEUE_RESIZE;
// if there is a grid resize happening, it means the viewport grid
// has been manipulated (e.g., panels closed, added, etc.) and we need
// to resize all viewports, so we will add a timeout here to make sure
// we don't double resize the viewports when viewports in the grid are
// resized individually
if (isGridResize) {
if (isEasyResize) {
this.performResize();
this.resetGridResizeTimeout();
this.resizeQueue = [];
@@ -400,11 +408,6 @@ class CornerstoneViewportService extends PubSubService implements IViewportServi
// and we would lose the presentation.
this.storePresentation({ viewportId: viewportInfo.getViewportId() });
// Todo: i don't like this here, move it
this.servicesManager.services.segmentationService.clearSegmentationRepresentations(
viewportInfo.getViewportId()
);
if (!viewportInfo) {
throw new Error('element is not enabled for the given viewportId');
}
@@ -506,6 +509,11 @@ class CornerstoneViewportService extends PubSubService implements IViewportServi
return this.viewportsById.get(viewportId);
}
public getOrientation(viewportId: string): string {
const viewportInfo = this.getViewportInfo(viewportId);
return viewportInfo.getOrientation();
}
/**
* Looks through the viewports to see if the specified measurement can be
* displayed in one of the viewports. This function tries to get a "best fit"
@@ -754,8 +762,8 @@ class CornerstoneViewportService extends PubSubService implements IViewportServi
const { imageIds, displaySetInstanceUID } = data;
let volume = data.volume;
if (!volume) {
const displaySet = displaySetService.getDisplaySetByUID(displaySetInstanceUID);
const displaySet = displaySetService.getDisplaySetByUID(displaySetInstanceUID);
if (!volume && displaySet.images) {
volume = csToolsUtils.getOrCreateImageVolume(displaySet.images.map(image => image.imageId));
}
@@ -773,6 +781,8 @@ class CornerstoneViewportService extends PubSubService implements IViewportServi
volumeInputArray.push({
imageIds,
volumeId,
modality: displaySet.Modality,
displaySetInstanceUID,
blendMode: displaySetOptions.blendMode,
slabThickness: this._getSlabThickness(displaySetOptions, volumeId),
});
@@ -815,8 +825,15 @@ class CornerstoneViewportService extends PubSubService implements IViewportServi
const displaySetUIDs = viewportGridService.getDisplaySetsUIDsForViewport(viewport.id);
const displaySet = displaySetService.getDisplaySetByUID(displaySetUIDs[0]);
const displaySetModality = displaySet?.Modality;
// filter overlay display sets (e.g. segmentation) since they will get handled below via the segmentation service
const filteredVolumeInputArray = volumeInputArray.filter(volumeInput => {
const displaySet = displaySetService.getDisplaySetByUID(volumeInput.displaySetInstanceUID);
return !displaySet?.isOverlayDisplaySet;
});
// Todo: use presentations states
const volumesProperties = volumeInputArray.map((volumeInput, index) => {
const volumesProperties = filteredVolumeInputArray.map((volumeInput, index) => {
const { volumeId } = volumeInput;
const displaySetOption = displaySetOptions[index];
const { voi, voiInverted, colormap, displayPreset } = displaySetOption;
@@ -846,9 +863,29 @@ class CornerstoneViewportService extends PubSubService implements IViewportServi
});
// For SEG and RT viewports
const { addOverlayFn } = this._processExtraDisplaySetsForViewport(viewport) || {};
const { addOverlayFn, imageIds } = this._processExtraDisplaySetsForViewport(viewport) || {};
await viewport.setVolumes(volumeInputArray);
if (!filteredVolumeInputArray.length && addOverlayFn) {
// if there is no volume input array, and there is an addOverlayFn, means we need to take
// care of the background overlay display set first then the addOverlayFn will add the
// SEG displaySet
const sampleImageId = imageIds[0];
const backgroundDisplaySet = displaySetService.getDisplaySetsBy(
displaySet =>
!displaySet.isOverlayDisplaySet &&
displaySet.images.some(image => image.imageId === sampleImageId)
);
if (backgroundDisplaySet.length !== 1) {
throw new Error('Background display set not found');
}
await viewport.setVolumes([
{ volumeId: `${VOLUME_LOADER_SCHEME}:${backgroundDisplaySet[0].displaySetInstanceUID}` },
]);
} else {
await viewport.setVolumes(filteredVolumeInputArray);
}
if (addOverlayFn) {
addOverlayFn();
@@ -889,31 +926,30 @@ class CornerstoneViewportService extends PubSubService implements IViewportServi
// load any secondary displaySets
const displaySetInstanceUIDs = this.viewportsDisplaySets.get(viewport.id);
// Can be SEG or RTSTRUCT for now but not PMAP
const segOrRTSOverlayDisplaySet = displaySetInstanceUIDs
// Find overlay display sets (e.g. SEG, RTSTRUCT)
const overlayDisplaySet = displaySetInstanceUIDs
.map(displaySetService.getDisplaySetByUID)
.find(
displaySet =>
displaySet?.isOverlayDisplaySet && ['SEG', 'RTSTRUCT'].includes(displaySet.Modality)
);
.find(displaySet => displaySet?.isOverlayDisplaySet);
// if it is only the overlay displaySet, then we need to get the reference
// displaySet imageIds and set them as the imageIds for the viewport,
// here we can do some logic if the reference is missing
// then find the most similar match of displaySet instead
if (!segOrRTSOverlayDisplaySet) {
if (!overlayDisplaySet) {
return;
}
const referenceDisplaySet = displaySetService.getDisplaySetByUID(
segOrRTSOverlayDisplaySet.referencedDisplaySetInstanceUID
);
const imageIds = referenceDisplaySet.images.map(image => image.imageId);
let imageIds;
if (overlayDisplaySet.referencedDisplaySetInstanceUID) {
const referenceDisplaySet = displaySetService.getDisplaySetByUID(
overlayDisplaySet.referencedDisplaySetInstanceUID
);
imageIds = referenceDisplaySet.images.map(image => image.imageId);
}
return {
imageIds,
addOverlayFn: () =>
this.addOverlayRepresentationForDisplaySet(segOrRTSOverlayDisplaySet, viewport),
addOverlayFn: () => this.addOverlayRepresentationForDisplaySet(overlayDisplaySet, viewport),
};
}
@@ -35,9 +35,8 @@ export interface IViewportService {
/**
* It creates a resize observer for the viewport element, and observes
* the element for resizing events
* @param {*} elementRef
*/
resize(isGridResize: boolean): void;
resize(): void;
/**
* Removes the viewport from cornerstone, and destroys the rendering engine
*/
@@ -316,6 +316,10 @@ class ViewportInfo {
return this.viewportOptions.orientation;
}
public setOrientation(orientation: Enums.OrientationAxis): void {
this.viewportOptions.orientation = orientation;
}
public getDisplayArea(): Types.DisplayArea {
return this.viewportOptions.displayArea;
}
@@ -1,5 +1,3 @@
import { CommandsManager } from '@ohif/core';
export type ViewportPreset = {
name: string;
gradientOpacity: string;
@@ -15,8 +13,6 @@ export type ViewportPreset = {
export type VolumeRenderingPresetsProps = {
viewportId: string;
servicesManager: AppTypes.ServicesManager;
commandsManager: CommandsManager;
volumeRenderingPresets: ViewportPreset[];
};
@@ -24,13 +20,10 @@ export type VolumeRenderingPresetsContentProps = {
presets: ViewportPreset[];
onClose: () => void;
viewportId: string;
commandsManager: CommandsManager;
};
export type VolumeRenderingOptionsProps = {
viewportId: string;
commandsManager: CommandsManager;
servicesManager: AppTypes.ServicesManager;
volumeRenderingQualityRange: VolumeRenderingQualityRange;
};
@@ -42,27 +35,19 @@ export type VolumeRenderingQualityRange = {
export type VolumeRenderingQualityProps = {
viewportId: string;
commandsManager: CommandsManager;
servicesManager: AppTypes.ServicesManager;
volumeRenderingQualityRange: VolumeRenderingQualityRange;
};
export type VolumeShiftProps = {
viewportId: string;
commandsManager: CommandsManager;
servicesManager: AppTypes.ServicesManager;
};
export type VolumeShadeProps = {
viewportId: string;
commandsManager: CommandsManager;
servicesManager: AppTypes.ServicesManager;
onClickShade?: (bool: boolean) => void;
};
export type VolumeLightingProps = {
viewportId: string;
commandsManager: CommandsManager;
servicesManager: AppTypes.ServicesManager;
hasShade: boolean;
};
@@ -1,5 +1,10 @@
import * as CornerstoneCacheService from './CornerstoneCacheService';
import type CornerstoneServices from './CornerstoneServices';
export { type SyncGroup } from '../services/SyncGroupService';
export {
type ViewportActionCornerLocation,
type ViewportActionCornerService,
type CustomizationService,
} from '../hooks/useViewportActionCorners';
export type { CornerstoneCacheService, CornerstoneServices };
@@ -180,15 +180,12 @@ const CornerstoneViewportDownloadForm = ({
const toolGroup = ToolGroupManager.getToolGroupForViewport(activeViewportId, renderingEngineId);
toolGroup.addViewport(downloadViewportId, renderingEngineId);
Object.keys(toolGroup.getToolInstances()).forEach(toolName => {
if (show && toolName !== 'Crosshairs') {
try {
toolGroup.setToolEnabled(toolName);
} catch (error) {
console.debug('Error enabling tool:', error);
}
} else {
toolGroup.setToolDisabled(toolName);
const toolInstances = toolGroup.getToolInstances();
const toolInstancesArray = Object.values(toolInstances);
toolInstancesArray.forEach(toolInstance => {
if (toolInstance.constructor.isAnnotation !== false) {
toolGroup.setToolEnabled(toolInstance.toolName);
}
});
};
@@ -13,6 +13,15 @@ const colormaps = [
RGBPoints: [0, 1, 1, 1, 1, 0, 0, 0],
description: 'X Ray',
},
{
ColorSpace: 'RGB',
Name: 'Isodose',
NanColor: [1, 0, 0],
RGBPoints: [
0, 0, 1, 0, 0.1, 0.5, 1, 0, 0.2, 1, 1, 0, 0.3, 1, 0.66, 0, 0.4, 1, 0.33, 0, 0.5, 1, 0, 0,
],
description: 'Isodose',
},
{
ColorSpace: 'RGB',
Name: 'hsv',
@@ -24,9 +24,13 @@ export function getViewportPresentations(
const { lutPresentationId, positionPresentationId, segmentationPresentationId } = presentationIds;
const positionPresentation = positionPresentationStore[positionPresentationId];
const lutPresentation = lutPresentationStore[lutPresentationId];
const segmentationPresentation = segmentationPresentationStore[segmentationPresentationId];
return {
positionPresentation: positionPresentationStore[positionPresentationId],
lutPresentation: lutPresentationStore[lutPresentationId],
segmentationPresentation: segmentationPresentationStore[segmentationPresentationId],
positionPresentation,
lutPresentation,
segmentationPresentation,
};
}
+218
View File
@@ -3,6 +3,224 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [3.11.0-beta.26](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.25...v3.11.0-beta.26) (2025-05-07)
**Note:** Version bump only for package @ohif/extension-default
# [3.11.0-beta.25](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.24...v3.11.0-beta.25) (2025-05-06)
**Note:** Version bump only for package @ohif/extension-default
# [3.11.0-beta.24](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.23...v3.11.0-beta.24) (2025-05-05)
### Features
* **data overlay:** add foreground background control plus orientation change in the UI and add RTDOSE support ([#5007](https://github.com/OHIF/Viewers/issues/5007)) ([cd4e640](https://github.com/OHIF/Viewers/commit/cd4e6408bc2964e5ee27427cdc328751eaf4d088))
# [3.11.0-beta.23](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.22...v3.11.0-beta.23) (2025-05-05)
**Note:** Version bump only for package @ohif/extension-default
# [3.11.0-beta.22](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.21...v3.11.0-beta.22) (2025-05-02)
### Features
* improve menu modals customizations ([#5011](https://github.com/OHIF/Viewers/issues/5011)) ([7dbe5e3](https://github.com/OHIF/Viewers/commit/7dbe5e37b06056fdd5262a131417013d112c3d30))
# [3.11.0-beta.21](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.20...v3.11.0-beta.21) (2025-05-02)
**Note:** Version bump only for package @ohif/extension-default
# [3.11.0-beta.20](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.19...v3.11.0-beta.20) (2025-05-01)
**Note:** Version bump only for package @ohif/extension-default
# [3.11.0-beta.19](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.18...v3.11.0-beta.19) (2025-05-01)
**Note:** Version bump only for package @ohif/extension-default
# [3.11.0-beta.18](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.17...v3.11.0-beta.18) (2025-04-30)
**Note:** Version bump only for package @ohif/extension-default
# [3.11.0-beta.17](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.16...v3.11.0-beta.17) (2025-04-29)
### Bug Fixes
* **referencelines:** and bring back the notification for worker updates but disallow duplication ([#5005](https://github.com/OHIF/Viewers/issues/5005)) ([98f8187](https://github.com/OHIF/Viewers/commit/98f8187dfeb8aec6eca145da75c855aec0da4e1d))
# [3.11.0-beta.16](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.15...v3.11.0-beta.16) (2025-04-29)
**Note:** Version bump only for package @ohif/extension-default
# [3.11.0-beta.15](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.14...v3.11.0-beta.15) (2025-04-22)
**Note:** Version bump only for package @ohif/extension-default
# [3.11.0-beta.14](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.13...v3.11.0-beta.14) (2025-04-22)
**Note:** Version bump only for package @ohif/extension-default
# [3.11.0-beta.13](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.12...v3.11.0-beta.13) (2025-04-22)
**Note:** Version bump only for package @ohif/extension-default
# [3.11.0-beta.12](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.11...v3.11.0-beta.12) (2025-04-22)
### Features
* Improve unsupported displayset messages ([#4979](https://github.com/OHIF/Viewers/issues/4979)) ([3b8faa8](https://github.com/OHIF/Viewers/commit/3b8faa8a76dde5088815431a947fec07db4a3c7f))
# [3.11.0-beta.11](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.10...v3.11.0-beta.11) (2025-04-18)
**Note:** Version bump only for package @ohif/extension-default
# [3.11.0-beta.10](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.9...v3.11.0-beta.10) (2025-04-17)
**Note:** Version bump only for package @ohif/extension-default
# [3.11.0-beta.9](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.8...v3.11.0-beta.9) (2025-04-17)
**Note:** Version bump only for package @ohif/extension-default
# [3.11.0-beta.8](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.7...v3.11.0-beta.8) (2025-04-16)
**Note:** Version bump only for package @ohif/extension-default
# [3.11.0-beta.7](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.6...v3.11.0-beta.7) (2025-04-16)
**Note:** Version bump only for package @ohif/extension-default
# [3.11.0-beta.6](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.5...v3.11.0-beta.6) (2025-04-16)
**Note:** Version bump only for package @ohif/extension-default
# [3.11.0-beta.5](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.4...v3.11.0-beta.5) (2025-04-14)
**Note:** Version bump only for package @ohif/extension-default
# [3.11.0-beta.4](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.3...v3.11.0-beta.4) (2025-04-11)
**Note:** Version bump only for package @ohif/extension-default
# [3.11.0-beta.3](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.2...v3.11.0-beta.3) (2025-04-11)
### Features
* expose scroll thumbnail into view ([#4941](https://github.com/OHIF/Viewers/issues/4941)) ([4926a95](https://github.com/OHIF/Viewers/commit/4926a9532c89011ef932fad1dbae643806e780a8))
# [3.11.0-beta.2](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.1...v3.11.0-beta.2) (2025-04-11)
### Bug Fixes
* make datasource selection work ([#4944](https://github.com/OHIF/Viewers/issues/4944)) ([a9b180c](https://github.com/OHIF/Viewers/commit/a9b180c5d427ec5a6823991cf0c6604be7af1cc2))
# [3.11.0-beta.1](https://github.com/OHIF/Viewers/compare/v3.11.0-beta.0...v3.11.0-beta.1) (2025-04-10)
**Note:** Version bump only for package @ohif/extension-default
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "@ohif/extension-default",
"version": "3.11.0-beta.1",
"version": "3.11.0-beta.26",
"description": "Common/default features and functionality for basic image viewing",
"author": "OHIF Core Team",
"license": "MIT",
@@ -32,8 +32,8 @@
"start": "yarn run dev"
},
"peerDependencies": {
"@ohif/core": "3.11.0-beta.1",
"@ohif/i18n": "3.11.0-beta.1",
"@ohif/core": "3.11.0-beta.26",
"@ohif/i18n": "3.11.0-beta.26",
"dcmjs": "*",
"dicomweb-client": "^0.10.4",
"prop-types": "^15.6.2",
@@ -7,17 +7,14 @@ import {
Icons,
Button,
} from '@ohif/ui-next';
import { useSystem } from '@ohif/core';
/**
* The default sub-menu appearance and setup is defined here, but this can be
* replaced by
*/
const getMenuItemsDefault = ({
commandsManager,
items,
servicesManager,
...props
}: withAppTypes) => {
const getMenuItemsDefault = ({ commandsManager, items, ...props }: withAppTypes) => {
const { servicesManager } = useSystem();
const { customizationService } = servicesManager.services;
// This allows replacing the default child item for menus, whereas the entire
@@ -36,7 +33,7 @@ const getMenuItemsDefault = ({
onClick: ({ commandsManager, ...props }: withAppTypes) => () => void;
};
}) => (
<DropdownMenuItem onClick={() => item.onClick({ commandsManager, ...props })}>
<DropdownMenuItem onClick={() => item.onClick({ commandsManager, servicesManager, ...props })}>
<div className="flex items-center gap-2">
{item.iconName && <Icons.ByName name={item.iconName} />}
<span>{item.label}</span>
@@ -76,12 +73,8 @@ const getMenuItemsDefault = ({
* @returns Component bound to the bindProps
*/
export default function MoreDropdownMenu(bindProps) {
const {
menuItemsKey,
getMenuItems = getMenuItemsDefault,
commandsManager,
servicesManager,
} = bindProps;
const { menuItemsKey, getMenuItems = getMenuItemsDefault, commandsManager } = bindProps;
const { servicesManager } = useSystem();
const { customizationService } = servicesManager.services;
const items = customizationService.getCustomization(menuItemsKey);
@@ -128,7 +128,9 @@ const DicomTagBrowser = ({
<div className="mb-6 flex flex-row items-start pl-1">
<div className="flex w-full flex-row items-start gap-4">
<div className="flex w-1/3 flex-col">
<span className="text-muted-foreground flex h-6 items-center text-xs">Series</span>
<span className="text-muted-foreground flex h-6 items-center pb-2 text-base">
Series
</span>
<Select
value={selectedDisplaySetInstanceUID}
onValueChange={value => onSelectChange({ value })}
@@ -154,7 +156,7 @@ const DicomTagBrowser = ({
</div>
{shouldShowInstanceList && (
<div className="mx-auto mt-0.5 flex w-1/4 flex-col">
<span className="text-muted-foreground flex h-6 items-center text-xs">
<span className="text-muted-foreground flex h-6 items-center pb-2 text-base">
Instance Number ({instanceNumber} of {activeDisplaySet?.images?.length})
</span>
<Slider
@@ -170,7 +172,7 @@ const DicomTagBrowser = ({
</div>
)}
<div className="ml-auto mr-1 flex w-1/3 flex-col">
<span className="text-muted-foreground flex h-6 items-center text-xs">
<span className="text-muted-foreground flex h-6 items-center pb-2 text-base">
Search metadata
</span>
<InputFilter
@@ -182,7 +184,7 @@ const DicomTagBrowser = ({
placeholder="Search metadata"
className="pl-9 pr-9"
/>
<InputFilter.ClearButton />
<InputFilter.ClearButton className="text-primary mr-0.5 p-0.5" />
</InputFilter>
</div>
</div>
@@ -8,6 +8,7 @@ import { Toolbar } from '../Toolbar/Toolbar';
import HeaderPatientInfo from './HeaderPatientInfo';
import { PatientInfoVisibility } from './HeaderPatientInfo/HeaderPatientInfo';
import { preserveQueryParameters } from '@ohif/app';
import { Types } from '@ohif/core';
function ViewerHeader({ appConfig }: withAppTypes<{ appConfig: AppTypes.Config }>) {
const { servicesManager, extensionManager, commandsManager } = useSystem();
@@ -38,28 +39,34 @@ function ViewerHeader({ appConfig }: withAppTypes<{ appConfig: AppTypes.Config }
const { t } = useTranslation();
const { show } = useModal();
const AboutModal = customizationService.getCustomization('ohif.aboutModal');
const UserPreferencesModal = customizationService.getCustomization('ohif.userPreferencesModal');
const AboutModal = customizationService.getCustomization(
'ohif.aboutModal'
) as Types.MenuComponentCustomization;
const UserPreferencesModal = customizationService.getCustomization(
'ohif.userPreferencesModal'
) as Types.MenuComponentCustomization;
const menuOptions = [
{
title: t('Header:About'),
title: AboutModal?.menuTitle ?? t('Header:About'),
icon: 'info',
onClick: () =>
show({
content: AboutModal,
title: t('AboutModal:About OHIF Viewer'),
containerClassName: 'max-w-md',
title: AboutModal?.title ?? t('AboutModal:About OHIF Viewer'),
containerClassName: AboutModal?.containerClassName ?? 'max-w-md',
}),
},
{
title: t('Header:Preferences'),
title: UserPreferencesModal.menuTitle ?? t('Header:Preferences'),
icon: 'settings',
onClick: () =>
show({
content: UserPreferencesModal,
title: t('UserPreferencesModal:User preferences'),
containerClassName: 'flex max-w-4xl p-6 flex-col',
title: UserPreferencesModal.title ?? t('UserPreferencesModal:User preferences'),
containerClassName:
UserPreferencesModal?.containerClassName ?? 'flex max-w-4xl p-6 flex-col',
}),
},
];
@@ -4,7 +4,7 @@ const collapsedOutsideBorderSize = 4;
const collapsedWidth = 25;
const rightPanelInitialExpandedWidth = 280;
const leftPanelInitialExpandedWidth = 292;
const leftPanelInitialExpandedWidth = 282;
const panelGroupDefinition = {
groupId: 'viewerLayoutResizablePanelGroup',
+145 -13
View File
@@ -3,6 +3,12 @@ import { Types, DicomMetadataStore } from '@ohif/core';
import { ContextMenuController } from './CustomizableContextMenu';
import DicomTagBrowser from './DicomTagBrowser/DicomTagBrowser';
import reuseCachedLayouts from './utils/reuseCachedLayouts';
import {
configureViewportForLayerAddition,
configureViewportForLayerRemoval,
canAddDisplaySetToViewport,
DERIVED_OVERLAY_MODALITIES,
} from './utils/layerConfigurationUtils';
import findViewportsByPosition, {
findOrCreateViewport as layoutFindOrCreate,
} from './findViewportsByPosition';
@@ -51,6 +57,137 @@ const commandsModule = ({
const contextMenuController = new ContextMenuController(servicesManager, commandsManager);
const actions = {
/**
* Adds a display set as a layer to the specified viewport
*
* @param options.viewportId - The ID of the viewport to add the layer to
* @param options.displaySetInstanceUID - The UID of the display set to add as a layer
* @param options.removeFirst - Optional flag to remove the display set first if it's already added
*/
addDisplaySetAsLayer: ({ viewportId, displaySetInstanceUID, removeFirst = false }) => {
if (!viewportId || !displaySetInstanceUID) {
console.warn('Missing required parameters for addDisplaySetAsLayer command');
return;
}
const { displaySetService, viewportGridService, hangingProtocolService } =
servicesManager.services;
// Get the display set
const displaySet = displaySetService.getDisplaySetByUID(displaySetInstanceUID);
if (!displaySet) {
return;
}
// Get current display sets for the viewport
const currentDisplaySetUIDs = viewportGridService.getDisplaySetsUIDsForViewport(viewportId);
// Check if we can add this display set to the viewport
const canAdd = canAddDisplaySetToViewport({
viewportId,
displaySetInstanceUID,
servicesManager,
});
if (!canAdd) {
return;
}
// Add the display set to the viewport
const updatedViewports = hangingProtocolService.getViewportsRequireUpdate(
viewportId,
displaySetInstanceUID
);
// Configure each viewport for layer addition
updatedViewports.forEach(viewport => {
configureViewportForLayerAddition({
viewport,
displaySetInstanceUID,
currentDisplaySetUIDs,
servicesManager,
});
});
// Update position presentation
commandsManager.runCommand('updateStoredPositionPresentation', {
viewportId,
displaySetInstanceUIDs: updatedViewports[0].displaySetInstanceUIDs,
});
// Run command to update viewports
commandsManager.run('setDisplaySetsForViewports', {
viewportsToUpdate: updatedViewports,
});
},
/**
* Removes a display set layer from the specified viewport
*
* @param options.viewportId - The ID of the viewport to remove the layer from
* @param options.displaySetInstanceUID - The UID of the display set to remove
*/
removeDisplaySetLayer: ({ viewportId, displaySetInstanceUID }) => {
if (!viewportId || !displaySetInstanceUID) {
console.warn('Missing required parameters for removeDisplaySetLayer command');
return;
}
const {
displaySetService,
viewportGridService,
hangingProtocolService,
segmentationService,
} = servicesManager.services;
// Get the display set
const displaySet = displaySetService.getDisplaySetByUID(displaySetInstanceUID);
if (!displaySet) {
return;
}
// Get current display sets for the viewport
const currentDisplaySetUIDs = viewportGridService.getDisplaySetsUIDsForViewport(viewportId);
// If the display set is not in the viewport, no need to remove it
if (!currentDisplaySetUIDs.includes(displaySetInstanceUID)) {
return;
}
// Check if it's a segmentation and handle accordingly
const isSegmentation = DERIVED_OVERLAY_MODALITIES.includes(displaySet.Modality);
if (isSegmentation) {
segmentationService.removeSegmentationRepresentations(viewportId, {
segmentationId: displaySetInstanceUID,
});
}
const updatedViewports = hangingProtocolService.getViewportsRequireUpdate(
viewportId,
displaySetInstanceUID
);
// Configure each viewport for layer removal
updatedViewports.forEach(viewport => {
configureViewportForLayerRemoval({
viewport,
displaySetInstanceUID,
currentDisplaySetUIDs,
servicesManager,
});
});
// Update position presentation
commandsManager.runCommand('updateStoredPositionPresentation', {
viewportId,
displaySetInstanceUIDs: updatedViewports[0].displaySetInstanceUIDs,
});
// Update the viewports
commandsManager.run('setDisplaySetsForViewports', {
viewportsToUpdate: updatedViewports,
});
},
/**
* Runs a command in multi-monitor mode. No-op if not multi-monitor.
*/
@@ -536,7 +673,11 @@ const commandsModule = ({
const { activeViewportId, viewports } = viewportGridService.getState();
const activeViewport = viewports.get(activeViewportId);
const activeDisplaySetInstanceUID = activeViewport.displaySetInstanceUIDs[0];
const activeDisplaySetInstanceUID = activeViewport?.displaySetInstanceUIDs?.[0];
if (!activeDisplaySetInstanceUID) {
return;
}
const thumbnailList = document.querySelector('#ohif-thumbnail-list');
@@ -544,24 +685,12 @@ const commandsModule = ({
return;
}
const thumbnailListBounds = thumbnailList.getBoundingClientRect();
const thumbnail = document.querySelector(`#thumbnail-${activeDisplaySetInstanceUID}`);
if (!thumbnail) {
return;
}
const thumbnailBounds = thumbnail.getBoundingClientRect();
// This only handles a vertical thumbnail list.
if (
thumbnailBounds.top >= thumbnailListBounds.top &&
thumbnailBounds.top <= thumbnailListBounds.bottom
) {
return;
}
thumbnail.scrollIntoView({ behavior: 'smooth' });
},
@@ -651,6 +780,9 @@ const commandsModule = ({
toggleOneUp: actions.toggleOneUp,
openDICOMTagViewer: actions.openDICOMTagViewer,
updateViewportDisplaySet: actions.updateViewportDisplaySet,
scrollActiveThumbnailIntoView: actions.scrollActiveThumbnailIntoView,
addDisplaySetAsLayer: actions.addDisplaySetAsLayer,
removeDisplaySetLayer: actions.removeDisplaySetLayer,
};
return {
@@ -1,5 +1,5 @@
import React, { useEffect, useMemo, useState } from 'react';
import { InputDialog, cn } from '@ohif/ui-next';
import React, { useCallback, useEffect, useMemo, useState } from 'react';
import { InputDialog } from '@ohif/ui-next';
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@ohif/ui-next';
import { useSystem } from '@ohif/core';
@@ -54,19 +54,19 @@ function ReportDialog({ dataSources, hide, onSave, onCancel }: ReportDialogProps
setReportName(newReportName);
}, [selectedSeries, seriesOptions]);
const handleSave = () => {
const handleSave = useCallback(() => {
onSave({
reportName,
dataSource: selectedDataSource,
series: selectedSeries,
});
hide();
};
}, [selectedDataSource, selectedSeries, reportName, hide, onSave]);
const handleCancel = () => {
const handleCancel = useCallback(() => {
onCancel();
hide();
};
}, [onCancel, hide]);
const showDataSourceSelect = dataSources?.length > 1;
@@ -14,6 +14,26 @@ export default {
});
},
},
{
id: 'addAsLayer',
label: 'Add as Layer',
iconName: 'ViewportViews',
onClick: ({ commandsManager, displaySetInstanceUID, servicesManager }: withAppTypes) => {
const { viewportGridService } = servicesManager.services;
// Get the active viewport
const { activeViewportId } = viewportGridService.getState();
if (!activeViewportId) {
return;
}
// Use the new command to add the display set as a layer
commandsManager.runCommand('addDisplaySetAsLayer', {
viewportId: activeViewportId,
displaySetInstanceUID,
});
},
},
],
'studyBrowser.sortFunctions': [
{
@@ -6,9 +6,21 @@ import { DisplaySetMessage, DisplaySetMessageList } from '@ohif/core';
export default function getDisplaySetsFromUnsupportedSeries(instances) {
const imageSet = new ImageSet(instances);
const messages = new DisplaySetMessageList();
messages.addMessage(DisplaySetMessage.CODES.UNSUPPORTED_DISPLAYSET);
const instance = instances[0];
if (!instances.length) {
messages.addMessage(DisplaySetMessage.CODES.NO_VALID_INSTANCES);
} else {
const sopClassUid = instance.SOPClassUID;
if (sopClassUid) {
messages.addMessage(DisplaySetMessage.CODES.UNSUPPORTED_SOP_CLASS_UID, {
sopClassUid,
});
} else {
messages.addMessage(DisplaySetMessage.CODES.MISSING_SOP_CLASS_UID);
}
}
imageSet.setAttributes({
displaySetInstanceUID: imageSet.uid, // create a local alias for the imageSet UID
SeriesDate: instance.SeriesDate,
@@ -1,4 +1,5 @@
import { utils, classes } from '@ohif/core';
import i18n from '@ohif/i18n';
import { id } from './id';
import getDisplaySetMessages from './getDisplaySetMessages';
import getDisplaySetsFromUnsupportedSeries from './getDisplaySetsFromUnsupportedSeries';
@@ -99,6 +100,11 @@ const makeDisplaySet = instances => {
averageSpacingBetweenFrames: averageSpacingBetweenFrames || null,
isDynamicVolume,
dynamicVolumeInfo,
supportsWindowLevel: true,
label:
instance.SeriesDescription ||
`${i18n.t('Series')} ${instance.SeriesNumber} - ${i18n.t(instance.Modality)}`,
FrameOfReferenceUID: instance.FrameOfReferenceUID,
});
// Sort the images in this series if needed
@@ -260,6 +266,7 @@ const sopClassUids = [
sopClassDictionary.LegacyConvertedEnhancedPETImageStorage,
sopClassDictionary.RTImageStorage,
sopClassDictionary.EnhancedUSVolumeStorage,
sopClassDictionary.RTDoseStorage,
];
function getSopClassHandlerModule(appContextParam) {
@@ -1,4 +1,3 @@
import { ToolbarButton as ToolbarButtonLegacy } from '@ohif/ui';
import { ToolButton, utils } from '@ohif/ui-next';
import ToolbarLayoutSelectorWithServices from './Toolbar/ToolbarLayoutSelector';
@@ -33,28 +32,6 @@ export default function getToolbarModule({ commandsManager, servicesManager }: w
name: 'ohif.toolBoxButton',
defaultComponent: ToolBoxButtonWrapper,
},
// legacy will get removed in the future
// legacy will get removed in the future
// legacy will get removed in the future
// legacy will get removed in the future
// legacy will get removed in the future
// legacy will get removed in the future
{
name: 'ohif.radioGroup',
defaultComponent: ToolbarButtonLegacy,
},
{
name: 'ohif.buttonGroup',
defaultComponent: ToolbarButtonGroupWithServicesLegacy,
},
{
name: 'ohif.divider',
defaultComponent: ToolbarDividerLegacy,
},
{
name: 'ohif.splitButton',
defaultComponent: ToolbarSplitButtonWithServicesLegacy,
},
// others
{
name: 'ohif.layoutSelector',
+1 -10
View File
@@ -1,15 +1,6 @@
import { CommandsManager, ExtensionManager } from '@ohif/core';
import LineChartViewport from './Components/LineChartViewport/index';
const getViewportModule = ({
servicesManager,
commandsManager,
extensionManager,
}: {
servicesManager: AppTypes.ServicesManager;
commandsManager: CommandsManager;
extensionManager: ExtensionManager;
}) => {
const getViewportModule = () => {
return [
{
name: 'chartViewport',
+33 -4
View File
@@ -22,11 +22,11 @@ export default function init({
cineService.EVENTS.CINE_STATE_CHANGED,
]);
// Add
DicomMetadataStore.subscribe(DicomMetadataStore.EVENTS.INSTANCES_ADDED, handlePETImageMetadata);
DicomMetadataStore.subscribe(DicomMetadataStore.EVENTS.INSTANCES_ADDED, handleScalingModules);
// If the metadata for PET has changed by the user (e.g. manually changing the PatientWeight)
// we need to recalculate the SUV Scaling Factors
DicomMetadataStore.subscribe(DicomMetadataStore.EVENTS.SERIES_UPDATED, handlePETImageMetadata);
DicomMetadataStore.subscribe(DicomMetadataStore.EVENTS.SERIES_UPDATED, handleScalingModules);
// Adds extra custom attributes for use by hanging protocols
registerHangingProtocolAttributes({ servicesManager });
@@ -71,7 +71,7 @@ export default function init({
});
}
const handlePETImageMetadata = ({ SeriesInstanceUID, StudyInstanceUID }) => {
const handleScalingModules = ({ SeriesInstanceUID, StudyInstanceUID }) => {
const { instances } = DicomMetadataStore.getSeries(StudyInstanceUID, SeriesInstanceUID);
if (!instances?.length) {
@@ -80,12 +80,41 @@ const handlePETImageMetadata = ({ SeriesInstanceUID, StudyInstanceUID }) => {
const modality = instances[0].Modality;
if (!modality || modality !== 'PT') {
const allowedModality = ['PT', 'RTDOSE'];
if (!allowedModality.includes(modality)) {
return;
}
const imageIds = instances.map(instance => instance.imageId);
const instanceMetadataArray = [];
if (modality === 'RTDOSE') {
const DoseGridScaling = instances[0].DoseGridScaling;
const DoseSummation = instances[0].DoseSummation;
const DoseType = instances[0].DoseType;
const DoseUnit = instances[0].DoseUnit;
const NumberOfFrames = instances[0].NumberOfFrames;
const imageId = imageIds[0];
// add scaling module to the metadata
// since RTDOSE is always a multiframe we should add the scaling module to each frame
for (let i = 0; i < NumberOfFrames; i++) {
const frameIndex = i + 1;
// Todo: we should support other things like wadouri, local etc
const newImageId = `${imageId.replace(/\/frames\/\d+$/, '')}/frames/${frameIndex}`;
metadataProvider.addCustomMetadata(newImageId, 'scalingModule', {
DoseGridScaling,
DoseSummation,
DoseType,
DoseUnit,
});
}
return;
}
// try except block to prevent errors when the metadata is not correct
try {
imageIds.forEach(imageId => {
@@ -0,0 +1,157 @@
export const DERIVED_OVERLAY_MODALITIES = ['SEG', 'RTSTRUCT'];
export const DEFAULT_COLORMAP = 'hsv';
export const DEFAULT_OPACITY = 0.9;
export const DEFAULT_OPACITY_PERCENT = DEFAULT_OPACITY * 100;
/**
* Get modality-specific color and opacity settings from the customization service
*/
export function getModalityOverlayColormap(customizationService, modality) {
const modalityOverlayDefaultColorMaps = customizationService?.getCustomization(
'cornerstone.modalityOverlayDefaultColorMaps'
) || { defaultSettings: {} };
return (
modalityOverlayDefaultColorMaps.defaultSettings[modality] || {
colormap: DEFAULT_COLORMAP,
opacity: DEFAULT_OPACITY,
}
);
}
/**
* Create display set options based on modality and opacity settings
*/
export function createColormapOverlayDisplaySetOptions(displaySet, opacity, customizationService) {
if (displaySet.Modality === 'SEG') {
return {};
}
const modalitySettings = getModalityOverlayColormap(customizationService, displaySet.Modality);
return {
colormap: {
name: modalitySettings.colormap || DEFAULT_COLORMAP,
opacity: opacity / 100, // Convert from percentage to 0-1 range
},
};
}
/**
* Configure viewport for adding a display set layer
*/
export function configureViewportForLayerAddition(params: {
viewport: any;
displaySetInstanceUID: string;
currentDisplaySetUIDs: string[];
servicesManager: AppTypes.ServicesManager;
}): any {
const { viewport, displaySetInstanceUID, currentDisplaySetUIDs, servicesManager } = params;
const { cornerstoneViewportService, displaySetService, customizationService } =
servicesManager.services;
const { viewportId } = viewport;
// Set the display set UIDs for the viewport
const allDisplaySetInstanceUIDs = [...currentDisplaySetUIDs, displaySetInstanceUID];
viewport.displaySetInstanceUIDs = allDisplaySetInstanceUIDs;
if (!viewport.viewportOptions) {
viewport.viewportOptions = {};
}
const requestedLayerDisplaySet = displaySetService.getDisplaySetByUID(displaySetInstanceUID);
if (!viewport.viewportOptions.orientation) {
viewport.viewportOptions.orientation = cornerstoneViewportService.getOrientation(viewportId);
}
// Do not force volume for SEG and RTSTRUCT if there is only one display set
if (requestedLayerDisplaySet.isOverlayDisplaySet && currentDisplaySetUIDs.length === 1) {
viewport.viewportOptions.viewportType = 'stack';
} else {
viewport.viewportOptions.viewportType = 'volume';
}
// create same amount of display set options as the number of display set UIDs
const displaySetOptions = allDisplaySetInstanceUIDs.map((uid, index) => {
if (index === 0) {
// no colormap for background
return {};
}
const displaySet = displaySetService.getDisplaySetByUID(uid);
return createColormapOverlayDisplaySetOptions(displaySet, 90, customizationService);
});
viewport.displaySetOptions = displaySetOptions;
return viewport;
}
/**
* Configure viewport for removing a display set layer
*/
export function configureViewportForLayerRemoval(params: {
viewport: any;
displaySetInstanceUID: string;
currentDisplaySetUIDs: string[];
servicesManager: AppTypes.ServicesManager;
}): any {
const { viewport, displaySetInstanceUID, currentDisplaySetUIDs, servicesManager } = params;
const { cornerstoneViewportService, displaySetService } = servicesManager.services;
const { viewportId } = viewport;
// Filter out the display set to remove
viewport.displaySetInstanceUIDs = currentDisplaySetUIDs.filter(
uid => uid !== displaySetInstanceUID
);
if (!viewport.viewportOptions) {
viewport.viewportOptions = {};
}
viewport.viewportOptions.viewportType = 'volume';
// orientation
if (!viewport.viewportOptions.orientation) {
viewport.viewportOptions.orientation = cornerstoneViewportService.getOrientation(viewportId);
}
// Recreate the display set options
viewport.displaySetOptions = viewport.displaySetInstanceUIDs.map(() => {
// For simplicity, we're returning empty options for now
// In a more complete implementation, we would need to preserve existing display set options
return {};
});
return viewport;
}
/**
* Check if a display set can be added as a layer to the specified viewport
*/
export function canAddDisplaySetToViewport(params: {
viewportId: string;
displaySetInstanceUID: string;
servicesManager: AppTypes.ServicesManager;
}): boolean {
const { viewportId, displaySetInstanceUID, servicesManager } = params;
const { displaySetService, viewportGridService } = servicesManager.services;
// Check if the display set exists
const displaySet = displaySetService.getDisplaySetByUID(displaySetInstanceUID);
if (!displaySet) {
return false;
}
// Get current display sets in the viewport
const currentDisplaySetUIDs = viewportGridService.getDisplaySetsUIDsForViewport(viewportId);
// Check if the display set is already in the viewport
if (currentDisplaySetUIDs.includes(displaySetInstanceUID)) {
return false;
}
return true;
}
Loaded 100 of 766 files, more files were not shown because too many files have changed in this diff. Show more