Compare commits
107
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ff5ead6284 | ||
|
|
58c2cc70cf | ||
|
|
1aa4431121 | ||
|
|
4e5d5a7701 | ||
|
|
6b18aa085d | ||
|
|
740dd7fe5f | ||
|
|
5b75a6a56a | ||
|
|
3dba8dbc9f | ||
|
|
4471416c30 | ||
|
|
a7229bb022 | ||
|
|
55ea706768 | ||
|
|
6d6ce0ddae | ||
|
|
0f26588c4b | ||
|
|
446f58eb76 | ||
|
|
411553ee91 | ||
|
|
e0d74a6674 | ||
|
|
7d0a3e6ce7 | ||
|
|
29cfb2c299 | ||
|
|
94b0358f18 | ||
|
|
173f42010b | ||
|
|
c5a51607c9 | ||
|
|
57205703bb | ||
|
|
9c5043ca2c | ||
|
|
9899d3f623 | ||
|
|
62d6bf437b | ||
|
|
4dc8debb02 | ||
|
|
f72c47568b | ||
|
|
bdad2264ed | ||
|
|
1c991037ee | ||
|
|
44eafe16c1 | ||
|
|
9ca02b4071 | ||
|
|
0be3b950b7 | ||
|
|
186e9caeaa | ||
|
|
5c17c262ea | ||
|
|
62797acd9a | ||
|
|
b15c002f60 | ||
|
|
9b4e66c868 | ||
|
|
02f6744dab | ||
|
|
5c44fe06dc | ||
|
|
b71952ec86 | ||
|
|
834ae3c762 | ||
|
|
79fab8d70d | ||
|
|
ace476649f | ||
|
|
58aa0d1eaa | ||
|
|
bb45cb24b5 | ||
|
|
2a07964002 | ||
|
|
16233d980f | ||
|
|
9413219206 | ||
|
|
14d62ce180 | ||
|
|
a0556b08be | ||
|
|
bee60a5482 | ||
|
|
5c496c7f02 | ||
|
|
29bd87c8c0 | ||
|
|
0c8ab01d34 | ||
|
|
f15887c9bf | ||
|
|
2481208f31 | ||
|
|
74c92fa601 | ||
|
|
beb6a73bc7 | ||
|
|
000e42e9a6 | ||
|
|
9ad7623a25 | ||
|
|
eb564aec5e | ||
|
|
35bfc9b0cc | ||
|
|
ab440f086a | ||
|
|
1d33bcf229 | ||
|
|
76f7d4e23a | ||
|
|
afa9131da1 | ||
|
|
5b5bca5e91 | ||
|
|
1f11c83006 | ||
|
|
1dd4f60034 | ||
|
|
2013c87ddb | ||
|
|
1009c60911 | ||
|
|
b517944cb5 | ||
|
|
67d6622ae4 | ||
|
|
a1544432db | ||
|
|
e0e1763472 | ||
|
|
b844b56839 | ||
|
|
8b9445c442 | ||
|
|
8193311841 | ||
|
|
c83710fa78 | ||
|
|
e07a87dcae | ||
|
|
2bc0647861 | ||
|
|
2a01dcce29 | ||
|
|
1df4f843fe | ||
|
|
cf0b561a49 | ||
|
|
f1675559ee | ||
|
|
ffd9ec3273 | ||
|
|
66e6d73ad6 | ||
|
|
07c45d0364 | ||
|
|
503780247c | ||
|
|
82c4a0a8d0 | ||
|
|
6a523ff78d | ||
|
|
170e860aa1 | ||
|
|
3e1d50c8d2 | ||
|
|
05b80305dc | ||
|
|
4620cc3acf | ||
|
|
cf8f4aa120 | ||
|
|
f75273ece6 | ||
|
|
42aa2df626 | ||
|
|
be6f3db1ce | ||
|
|
c41dbc8236 | ||
|
|
53694da216 | ||
|
|
9d68b6ef31 | ||
|
|
2300c70314 | ||
|
|
86e924fb88 | ||
|
|
e988216b2b | ||
|
|
23c8fdca00 | ||
|
|
faea09e7ef |
No files matched your search
@@ -119,7 +119,8 @@ jobs:
|
||||
echo "🔍 Running bun audit for security vulnerabilities..."
|
||||
echo "Checking for HIGH-RISK vulnerabilities..."
|
||||
|
||||
if bun audit --audit-level high; then
|
||||
# Ignore CVE-2025-64756 (aka GHSA-5j98-mcp5-4vw2) - glob is not used on the command line
|
||||
if bun audit --ignore=GHSA-5j98-mcp5-4vw2 --audit-level high; then
|
||||
echo "✅ No high-risk vulnerabilities found"
|
||||
echo "🎉 Security audit passed!"
|
||||
else
|
||||
@@ -135,7 +136,10 @@ jobs:
|
||||
echo " 5. Re-run: bun audit --audit-level high"
|
||||
echo ""
|
||||
echo "📋 Full audit report:"
|
||||
bun audit --audit-level low || true
|
||||
|
||||
# Ignore CVE-2025-64756 (aka GHSA-5j98-mcp5-4vw2) - glob is not used on the command line
|
||||
bun audit --ignore=GHSA-5j98-mcp5-4vw2 --audit-level low || true
|
||||
|
||||
echo ""
|
||||
echo "❌ This build cannot proceed until high-risk vulnerabilities are resolved."
|
||||
exit 1
|
||||
@@ -371,7 +375,7 @@ jobs:
|
||||
name: Export Display Variable
|
||||
command: export DISPLAY=:99
|
||||
- cypress/install:
|
||||
install-command: yarn install --no-save
|
||||
install-command: yarn install --frozen-lockfile --no-save
|
||||
- cypress/run-tests:
|
||||
cypress-command: |
|
||||
npx wait-on@latest http://localhost:3000 && cd platform/app && npx cypress run --record --parallel
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
name: Playwright Tests
|
||||
on:
|
||||
pull_request:
|
||||
branches: [master]
|
||||
branches: [master, release/*]
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
@@ -23,6 +23,8 @@ jobs:
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
- name: Install Yarn
|
||||
run: npm install -g yarn@1.22.22
|
||||
- name: Install dependencies
|
||||
run: bun install --frozen-lockfile
|
||||
- name: Install Playwright browsers
|
||||
|
||||
@@ -19,7 +19,7 @@ echo 'Web application built and copied'
|
||||
|
||||
# Build && Move Docusaurus Output (for the docs themselves)
|
||||
cd platform/docs
|
||||
yarn install
|
||||
yarn install --frozen-lockfile
|
||||
yarn run build
|
||||
cd ../..
|
||||
mkdir -p ./.netlify/www/docs
|
||||
|
||||
+358
@@ -3,6 +3,364 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [3.12.0-beta.97](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.96...v3.12.0-beta.97) (2025-12-03)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* skip VOI sync for empty viewports ([#5599](https://github.com/OHIF/Viewers/issues/5599)) ([1aa4431](https://github.com/OHIF/Viewers/commit/1aa443112186851855a638ab974a3060c3e28ea0))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.96](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.95...v3.12.0-beta.96) (2025-12-02)
|
||||
|
||||
**Note:** Version bump only for package ohif-monorepo-root
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.95](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.94...v3.12.0-beta.95) (2025-12-02)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* sculptor tool fixes ([#5595](https://github.com/OHIF/Viewers/issues/5595)) ([4471416](https://github.com/OHIF/Viewers/commit/4471416c30bf80d9f6766660d103f28c83ba7bb3))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.94](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.93...v3.12.0-beta.94) (2025-12-02)
|
||||
|
||||
**Note:** Version bump only for package ohif-monorepo-root
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.93](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.92...v3.12.0-beta.93) (2025-11-25)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* tooltips for tool settings ([#5586](https://github.com/OHIF/Viewers/issues/5586)) ([411553e](https://github.com/OHIF/Viewers/commit/411553ee916b8a63b2a934a7e1274bc449041016))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.92](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.91...v3.12.0-beta.92) (2025-11-24)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **docs:** Empty commit as an attempt to fix the docs build. ([#5582](https://github.com/OHIF/Viewers/issues/5582)) ([94b0358](https://github.com/OHIF/Viewers/commit/94b0358f18f81fa2822a208d68decfce076ef577))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.91](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.90...v3.12.0-beta.91) (2025-11-21)
|
||||
|
||||
**Note:** Version bump only for package ohif-monorepo-root
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.90](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.89...v3.12.0-beta.90) (2025-11-21)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **DicomUpload:** Switch to use ui-next components and colors ([#5576](https://github.com/OHIF/Viewers/issues/5576)) ([62d6bf4](https://github.com/OHIF/Viewers/commit/62d6bf437b167ac3e60a0f134869c9f96d199d0f))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.89](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.88...v3.12.0-beta.89) (2025-11-20)
|
||||
|
||||
**Note:** Version bump only for package ohif-monorepo-root
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.88](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.87...v3.12.0-beta.88) (2025-11-20)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Remove dead code and fix rectangle roi rehydration ([#5490](https://github.com/OHIF/Viewers/issues/5490)) ([9ca02b4](https://github.com/OHIF/Viewers/commit/9ca02b4071f77600f7dcd87930fecdcf3d1c249f))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.87](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.86...v3.12.0-beta.87) (2025-11-19)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **SegmentationStyle:** Fix inactive contour visibility and styling. ([#5563](https://github.com/OHIF/Viewers/issues/5563)) ([5c17c26](https://github.com/OHIF/Viewers/commit/5c17c262eaa5c1229033cfff4b3e7709e874611f))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.86](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.85...v3.12.0-beta.86) (2025-11-13)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **segmentation:** Allow for manually added label map and contour segmentations to be used as viewport data overlays. ([#5562](https://github.com/OHIF/Viewers/issues/5562)) ([02f6744](https://github.com/OHIF/Viewers/commit/02f6744dab993f16cf2516fd5f657e0743e3fa42))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.85](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.84...v3.12.0-beta.85) (2025-11-11)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **interpolation:** Auto accept interpolation when the interpolation process is completed. ([#5555](https://github.com/OHIF/Viewers/issues/5555)) ([834ae3c](https://github.com/OHIF/Viewers/commit/834ae3c762ca309187afebdf77bad1ad9a57f03f))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.84](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.83...v3.12.0-beta.84) (2025-11-10)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **dev:** update webpack proxy config syntax for Orthanc dev mode ([#5531](https://github.com/OHIF/Viewers/issues/5531)) ([58aa0d1](https://github.com/OHIF/Viewers/commit/58aa0d1eaae2e1a8103e6096864a4e5d7baf090e))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.83](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.82...v3.12.0-beta.83) (2025-11-06)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **metadata:** metadata access improvement ([#5292](https://github.com/OHIF/Viewers/issues/5292)) ([16233d9](https://github.com/OHIF/Viewers/commit/16233d980f17abfc30461b46c6b1888be10c7840))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.82](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.81...v3.12.0-beta.82) (2025-11-05)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **segmentation:** When synchronizing a segmentation representation, use the type of the source viewport segmentation to determine the type of the target viewport segmentation ([#5546](https://github.com/OHIF/Viewers/issues/5546)) ([a0556b0](https://github.com/OHIF/Viewers/commit/a0556b08be98e0bba950ad7b9b575d7b971104ba))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.81](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.80...v3.12.0-beta.81) (2025-11-05)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **SegmentationTools:** [Bug] Changes of brush/eraser radius with hotkey do not reflect on segmentation tool ([#5535](https://github.com/OHIF/Viewers/issues/5535)) ([29bd87c](https://github.com/OHIF/Viewers/commit/29bd87c8c0592a9a1350e687b862f5452627aece))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.80](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.79...v3.12.0-beta.80) (2025-11-05)
|
||||
|
||||
**Note:** Version bump only for package ohif-monorepo-root
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.79](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.78...v3.12.0-beta.79) (2025-10-30)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **security:** Exact versioning and docs dependabot alerts ([#5536](https://github.com/OHIF/Viewers/issues/5536)) ([000e42e](https://github.com/OHIF/Viewers/commit/000e42e9a6eaa4f0878cbf877f03b09a459ad18e))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.78](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.77...v3.12.0-beta.78) (2025-10-29)
|
||||
|
||||
**Note:** Version bump only for package ohif-monorepo-root
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.77](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.76...v3.12.0-beta.77) (2025-10-28)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **3DSegmentation:** [Bug] The viewports become blank when loading the seg file in advanced layout after closing the seg file from any other advanced layout ([#5505](https://github.com/OHIF/Viewers/issues/5505)) ([76f7d4e](https://github.com/OHIF/Viewers/commit/76f7d4e23aa359a3397b10adf0bf09df3763f190))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.76](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.75...v3.12.0-beta.76) (2025-10-27)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **security:** Addressed dependabot alert CVE-2025-59288 concerning playwright ([#5527](https://github.com/OHIF/Viewers/issues/5527)) ([1f11c83](https://github.com/OHIF/Viewers/commit/1f11c83006bff98cb8434f0662b4de55d5b9bf78))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.75](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.74...v3.12.0-beta.75) (2025-10-24)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **security:** For bun, enforce frozen lockfile. For yarn, strongly suggest using frozen lockfile. ([#5508](https://github.com/OHIF/Viewers/issues/5508)) ([1009c60](https://github.com/OHIF/Viewers/commit/1009c6091107d2db0768622120f916208a391343))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.74](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.73...v3.12.0-beta.74) (2025-10-23)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **jpeg2000:** Add 16-bit RGB support to JPEG2000 decoder ([#5519](https://github.com/OHIF/Viewers/issues/5519)) ([a154443](https://github.com/OHIF/Viewers/commit/a1544432db42bf77864bb7df2f757cae819a4b4d))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.73](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.72...v3.12.0-beta.73) (2025-10-22)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Fixed the tooltip text of the window button, it was mistakenly written in Chinese in the en-us json ([#5513](https://github.com/OHIF/Viewers/issues/5513)) ([8b9445c](https://github.com/OHIF/Viewers/commit/8b9445c442d0a40b7e4393614313192bd006461b))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.72](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.71...v3.12.0-beta.72) (2025-10-22)
|
||||
|
||||
**Note:** Version bump only for package ohif-monorepo-root
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.71](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.70...v3.12.0-beta.71) (2025-10-21)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **segmentation:** Fixed the manual scrolling of the segments list and implemented automatic scrolling to the active segment. ([#5510](https://github.com/OHIF/Viewers/issues/5510)) ([1df4f84](https://github.com/OHIF/Viewers/commit/1df4f843fe3c377643938563ff9e2e4f930d8537))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.70](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.69...v3.12.0-beta.70) (2025-10-20)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **rendering:** Fix palette color LUT conversion causing black images ([#5509](https://github.com/OHIF/Viewers/issues/5509)) ([ffd9ec3](https://github.com/OHIF/Viewers/commit/ffd9ec32730580e266ffae365a10ac3e524dd6cf))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.69](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.68...v3.12.0-beta.69) (2025-10-20)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Pass the correct sop uid + frame for rehydration to prevent associating rehydrated measurements with the wrong data ([#5506](https://github.com/OHIF/Viewers/issues/5506)) ([5037802](https://github.com/OHIF/Viewers/commit/503780247ce7228c602a539fd88ea374e7d0d42f)), closes [#2404](https://github.com/OHIF/Viewers/issues/2404)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.68](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.67...v3.12.0-beta.68) (2025-10-18)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **segmentation:** Lock all rehydrated segmentation segments when panelSegmentation.disableEditing is true. ([#5503](https://github.com/OHIF/Viewers/issues/5503)) ([170e860](https://github.com/OHIF/Viewers/commit/170e860aa1b786ab6056cf31076804302a2bc462))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.67](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.66...v3.12.0-beta.67) (2025-10-17)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **ErrorBoundary:** Allow for details to be shown in production. ([#5504](https://github.com/OHIF/Viewers/issues/5504)) ([4620cc3](https://github.com/OHIF/Viewers/commit/4620cc3acf89f218c3feaaf4c153a3dc8b023b23))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.66](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.65...v3.12.0-beta.66) (2025-10-17)
|
||||
|
||||
**Note:** Version bump only for package ohif-monorepo-root
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.65](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.64...v3.12.0-beta.65) (2025-10-17)
|
||||
|
||||
**Note:** Version bump only for package ohif-monorepo-root
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.64](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.63...v3.12.0-beta.64) (2025-10-15)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **docker:** Use bun version 1.2.23 for docker build. ([#5501](https://github.com/OHIF/Viewers/issues/5501)) ([86e924f](https://github.com/OHIF/Viewers/commit/86e924fb88cc146f91c7d826ee071e451de47942))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.63](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.62...v3.12.0-beta.63) (2025-10-15)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Segmentation not displayed and unable to draw segments in volume viewport after converting Labelmap to Surface ([#5488](https://github.com/OHIF/Viewers/issues/5488)) ([faea09e](https://github.com/OHIF/Viewers/commit/faea09e7ef2667aad5cef1efc2a6516677ca2915))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.62](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.61...v3.12.0-beta.62) (2025-10-15)
|
||||
|
||||
|
||||
|
||||
+1
-1
@@ -49,7 +49,7 @@ RUN apt-get update && apt-get install -y build-essential python3
|
||||
|
||||
RUN mkdir /usr/src/app
|
||||
WORKDIR /usr/src/app
|
||||
RUN npm install -g bun
|
||||
RUN npm install -g bun@1.2.23
|
||||
RUN npm install -g lerna@7.4.2
|
||||
ENV PATH=/usr/src/app/node_modules/.bin:$PATH
|
||||
|
||||
|
||||
@@ -53,7 +53,7 @@ provided by the <a href="https://ohif.org/">Open Health Imaging Foundation (OHIF
|
||||
## About
|
||||
|
||||
The OHIF Viewer can retrieve
|
||||
and load images from most sources and formats; render sets in 2D, 3D, and
|
||||
and load images from most sources and formats, render sets in 2D, 3D, and
|
||||
reconstructed representations; allows for the manipulation, annotation, and
|
||||
serialization of observations; supports internationalization, OpenID Connect,
|
||||
offline use, hotkeys, and many more features.
|
||||
@@ -147,7 +147,14 @@ Here is a schematic representation of our development workflow:
|
||||
3. Navigate to the cloned project's directory
|
||||
4. Add this repo as a `remote` named `upstream`
|
||||
- `git remote add upstream https://github.com/OHIF/Viewers.git`
|
||||
5. `yarn install` to restore dependencies and link projects
|
||||
5. `yarn install --frozen-lockfile` to restore dependencies and link projects
|
||||
|
||||
:::danger
|
||||
In general run `yarn install` with the `--frozen-lockfile` flag to help avoid
|
||||
supply chain attacks by enforcing reproducible dependencies. That is, if the
|
||||
`yarn.lock` file is clean and does NOT reference compromised packages, then
|
||||
no compromised packages should land on your machine by using this flag.
|
||||
:::
|
||||
|
||||
#### To Develop
|
||||
|
||||
@@ -158,7 +165,7 @@ _From this repository's root directory:_
|
||||
yarn config set workspaces-experimental true
|
||||
|
||||
# Restore dependencies
|
||||
yarn install
|
||||
yarn install --frozen-lockfile
|
||||
```
|
||||
|
||||
## Commands
|
||||
|
||||
+283
@@ -3,6 +3,289 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [3.12.0-beta.97](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.96...v3.12.0-beta.97) (2025-12-03)
|
||||
|
||||
**Note:** Version bump only for package @externals/devDependencies
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.96](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.95...v3.12.0-beta.96) (2025-12-02)
|
||||
|
||||
**Note:** Version bump only for package @externals/devDependencies
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.95](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.94...v3.12.0-beta.95) (2025-12-02)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* sculptor tool fixes ([#5595](https://github.com/OHIF/Viewers/issues/5595)) ([4471416](https://github.com/OHIF/Viewers/commit/4471416c30bf80d9f6766660d103f28c83ba7bb3))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.94](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.93...v3.12.0-beta.94) (2025-12-02)
|
||||
|
||||
**Note:** Version bump only for package @externals/devDependencies
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.93](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.92...v3.12.0-beta.93) (2025-11-25)
|
||||
|
||||
**Note:** Version bump only for package @externals/devDependencies
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.92](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.91...v3.12.0-beta.92) (2025-11-24)
|
||||
|
||||
**Note:** Version bump only for package @externals/devDependencies
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.91](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.90...v3.12.0-beta.91) (2025-11-21)
|
||||
|
||||
**Note:** Version bump only for package @externals/devDependencies
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.90](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.89...v3.12.0-beta.90) (2025-11-21)
|
||||
|
||||
**Note:** Version bump only for package @externals/devDependencies
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.89](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.88...v3.12.0-beta.89) (2025-11-20)
|
||||
|
||||
**Note:** Version bump only for package @externals/devDependencies
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.88](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.87...v3.12.0-beta.88) (2025-11-20)
|
||||
|
||||
**Note:** Version bump only for package @externals/devDependencies
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.87](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.86...v3.12.0-beta.87) (2025-11-19)
|
||||
|
||||
**Note:** Version bump only for package @externals/devDependencies
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.86](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.85...v3.12.0-beta.86) (2025-11-13)
|
||||
|
||||
**Note:** Version bump only for package @externals/devDependencies
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.85](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.84...v3.12.0-beta.85) (2025-11-11)
|
||||
|
||||
**Note:** Version bump only for package @externals/devDependencies
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.84](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.83...v3.12.0-beta.84) (2025-11-10)
|
||||
|
||||
**Note:** Version bump only for package @externals/devDependencies
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.83](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.82...v3.12.0-beta.83) (2025-11-06)
|
||||
|
||||
**Note:** Version bump only for package @externals/devDependencies
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.82](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.81...v3.12.0-beta.82) (2025-11-05)
|
||||
|
||||
**Note:** Version bump only for package @externals/devDependencies
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.81](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.80...v3.12.0-beta.81) (2025-11-05)
|
||||
|
||||
**Note:** Version bump only for package @externals/devDependencies
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.80](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.79...v3.12.0-beta.80) (2025-11-05)
|
||||
|
||||
**Note:** Version bump only for package @externals/devDependencies
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.79](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.78...v3.12.0-beta.79) (2025-10-30)
|
||||
|
||||
**Note:** Version bump only for package @externals/devDependencies
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.78](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.77...v3.12.0-beta.78) (2025-10-29)
|
||||
|
||||
**Note:** Version bump only for package @externals/devDependencies
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.77](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.76...v3.12.0-beta.77) (2025-10-28)
|
||||
|
||||
**Note:** Version bump only for package @externals/devDependencies
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.76](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.75...v3.12.0-beta.76) (2025-10-27)
|
||||
|
||||
**Note:** Version bump only for package @externals/devDependencies
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.75](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.74...v3.12.0-beta.75) (2025-10-24)
|
||||
|
||||
**Note:** Version bump only for package @externals/devDependencies
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.74](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.73...v3.12.0-beta.74) (2025-10-23)
|
||||
|
||||
**Note:** Version bump only for package @externals/devDependencies
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.73](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.72...v3.12.0-beta.73) (2025-10-22)
|
||||
|
||||
**Note:** Version bump only for package @externals/devDependencies
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.72](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.71...v3.12.0-beta.72) (2025-10-22)
|
||||
|
||||
**Note:** Version bump only for package @externals/devDependencies
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.71](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.70...v3.12.0-beta.71) (2025-10-21)
|
||||
|
||||
**Note:** Version bump only for package @externals/devDependencies
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.70](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.69...v3.12.0-beta.70) (2025-10-20)
|
||||
|
||||
**Note:** Version bump only for package @externals/devDependencies
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.69](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.68...v3.12.0-beta.69) (2025-10-20)
|
||||
|
||||
**Note:** Version bump only for package @externals/devDependencies
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.68](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.67...v3.12.0-beta.68) (2025-10-18)
|
||||
|
||||
**Note:** Version bump only for package @externals/devDependencies
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.67](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.66...v3.12.0-beta.67) (2025-10-17)
|
||||
|
||||
**Note:** Version bump only for package @externals/devDependencies
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.66](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.65...v3.12.0-beta.66) (2025-10-17)
|
||||
|
||||
**Note:** Version bump only for package @externals/devDependencies
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.65](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.64...v3.12.0-beta.65) (2025-10-17)
|
||||
|
||||
**Note:** Version bump only for package @externals/devDependencies
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.64](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.63...v3.12.0-beta.64) (2025-10-15)
|
||||
|
||||
**Note:** Version bump only for package @externals/devDependencies
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.63](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.62...v3.12.0-beta.63) (2025-10-15)
|
||||
|
||||
**Note:** Version bump only for package @externals/devDependencies
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.62](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.61...v3.12.0-beta.62) (2025-10-15)
|
||||
|
||||
**Note:** Version bump only for package @externals/devDependencies
|
||||
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@externals/devDependencies",
|
||||
"description": "External dev dependencies - put dev build dependencies here",
|
||||
"version": "3.12.0-beta.62",
|
||||
"version": "3.12.0-beta.97",
|
||||
"license": "MIT",
|
||||
"private": true,
|
||||
"engines": {
|
||||
@@ -10,7 +10,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": "7.28.2",
|
||||
"@kitware/vtk.js": "32.12.0",
|
||||
"@kitware/vtk.js": "34.15.1",
|
||||
"clsx": "2.1.1",
|
||||
"core-js": "3.45.1",
|
||||
"moment": "2.30.1"
|
||||
|
||||
@@ -3,6 +3,286 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [3.12.0-beta.97](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.96...v3.12.0-beta.97) (2025-12-03)
|
||||
|
||||
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.96](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.95...v3.12.0-beta.96) (2025-12-02)
|
||||
|
||||
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.95](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.94...v3.12.0-beta.95) (2025-12-02)
|
||||
|
||||
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.94](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.93...v3.12.0-beta.94) (2025-12-02)
|
||||
|
||||
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.93](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.92...v3.12.0-beta.93) (2025-11-25)
|
||||
|
||||
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.92](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.91...v3.12.0-beta.92) (2025-11-24)
|
||||
|
||||
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.91](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.90...v3.12.0-beta.91) (2025-11-21)
|
||||
|
||||
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.90](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.89...v3.12.0-beta.90) (2025-11-21)
|
||||
|
||||
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.89](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.88...v3.12.0-beta.89) (2025-11-20)
|
||||
|
||||
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.88](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.87...v3.12.0-beta.88) (2025-11-20)
|
||||
|
||||
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.87](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.86...v3.12.0-beta.87) (2025-11-19)
|
||||
|
||||
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.86](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.85...v3.12.0-beta.86) (2025-11-13)
|
||||
|
||||
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.85](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.84...v3.12.0-beta.85) (2025-11-11)
|
||||
|
||||
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.84](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.83...v3.12.0-beta.84) (2025-11-10)
|
||||
|
||||
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.83](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.82...v3.12.0-beta.83) (2025-11-06)
|
||||
|
||||
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.82](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.81...v3.12.0-beta.82) (2025-11-05)
|
||||
|
||||
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.81](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.80...v3.12.0-beta.81) (2025-11-05)
|
||||
|
||||
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.80](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.79...v3.12.0-beta.80) (2025-11-05)
|
||||
|
||||
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.79](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.78...v3.12.0-beta.79) (2025-10-30)
|
||||
|
||||
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.78](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.77...v3.12.0-beta.78) (2025-10-29)
|
||||
|
||||
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.77](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.76...v3.12.0-beta.77) (2025-10-28)
|
||||
|
||||
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.76](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.75...v3.12.0-beta.76) (2025-10-27)
|
||||
|
||||
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.75](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.74...v3.12.0-beta.75) (2025-10-24)
|
||||
|
||||
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.74](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.73...v3.12.0-beta.74) (2025-10-23)
|
||||
|
||||
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.73](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.72...v3.12.0-beta.73) (2025-10-22)
|
||||
|
||||
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.72](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.71...v3.12.0-beta.72) (2025-10-22)
|
||||
|
||||
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.71](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.70...v3.12.0-beta.71) (2025-10-21)
|
||||
|
||||
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.70](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.69...v3.12.0-beta.70) (2025-10-20)
|
||||
|
||||
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.69](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.68...v3.12.0-beta.69) (2025-10-20)
|
||||
|
||||
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.68](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.67...v3.12.0-beta.68) (2025-10-18)
|
||||
|
||||
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.67](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.66...v3.12.0-beta.67) (2025-10-17)
|
||||
|
||||
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.66](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.65...v3.12.0-beta.66) (2025-10-17)
|
||||
|
||||
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.65](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.64...v3.12.0-beta.65) (2025-10-17)
|
||||
|
||||
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.64](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.63...v3.12.0-beta.64) (2025-10-15)
|
||||
|
||||
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.63](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.62...v3.12.0-beta.63) (2025-10-15)
|
||||
|
||||
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.62](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.61...v3.12.0-beta.62) (2025-10-15)
|
||||
|
||||
**Note:** Version bump only for package @externals/dicom-microscopy-viewer
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@externals/dicom-microscopy-viewer",
|
||||
"description": "External reference to dicom-microscopy-viewer",
|
||||
"version": "3.12.0-beta.62",
|
||||
"version": "3.12.0-beta.97",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"dicom-microscopy-viewer": "0.48.6"
|
||||
|
||||
+2
-2
@@ -38,8 +38,8 @@
|
||||
"resolutions": {
|
||||
"**/@babel/runtime": "7.28.2",
|
||||
"commander": "8.3.0",
|
||||
"dcmjs": "0.43.1",
|
||||
"dicomweb-client": ">=0.10.4",
|
||||
"dcmjs": "0.45.0",
|
||||
"dicomweb-client": "0.10.4",
|
||||
"nth-check": "2.1.1",
|
||||
"trim-newlines": "5.0.0",
|
||||
"glob-parent": "6.0.2",
|
||||
|
||||
@@ -47,15 +47,15 @@
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@percy/cypress": "3.1.6",
|
||||
"@playwright/test": "1.54.1",
|
||||
"@playwright/test": "1.56.1",
|
||||
},
|
||||
},
|
||||
"addOns/externals/devDependencies": {
|
||||
"name": "@externals/devDependencies",
|
||||
"version": "3.12.0-beta.58",
|
||||
"version": "3.12.0-beta.94",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "7.28.2",
|
||||
"@kitware/vtk.js": "32.12.0",
|
||||
"@kitware/vtk.js": "34.15.1",
|
||||
"clsx": "2.1.1",
|
||||
"core-js": "3.45.1",
|
||||
"moment": "2.30.1",
|
||||
@@ -137,24 +137,24 @@
|
||||
},
|
||||
"addOns/externals/dicom-microscopy-viewer": {
|
||||
"name": "@externals/dicom-microscopy-viewer",
|
||||
"version": "3.12.0-beta.58",
|
||||
"version": "3.12.0-beta.94",
|
||||
"dependencies": {
|
||||
"dicom-microscopy-viewer": "0.48.6",
|
||||
},
|
||||
},
|
||||
"extensions/cornerstone": {
|
||||
"name": "@ohif/extension-cornerstone",
|
||||
"version": "3.12.0-beta.58",
|
||||
"version": "3.12.0-beta.94",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "7.28.2",
|
||||
"@cornerstonejs/adapters": "4.5.5",
|
||||
"@cornerstonejs/ai": "4.5.5",
|
||||
"@cornerstonejs/core": "4.5.5",
|
||||
"@cornerstonejs/labelmap-interpolation": "4.5.5",
|
||||
"@cornerstonejs/polymorphic-segmentation": "4.5.5",
|
||||
"@cornerstonejs/tools": "4.5.5",
|
||||
"@cornerstonejs/adapters": "4.12.6",
|
||||
"@cornerstonejs/ai": "4.12.6",
|
||||
"@cornerstonejs/core": "4.12.6",
|
||||
"@cornerstonejs/labelmap-interpolation": "4.12.6",
|
||||
"@cornerstonejs/polymorphic-segmentation": "4.12.6",
|
||||
"@cornerstonejs/tools": "4.12.6",
|
||||
"@itk-wasm/morphological-contour-interpolation": "1.1.0",
|
||||
"@kitware/vtk.js": "32.12.0",
|
||||
"@kitware/vtk.js": "34.15.1",
|
||||
"html2canvas": "1.4.1",
|
||||
"lodash.compact": "3.0.1",
|
||||
"lodash.debounce": "4.0.8",
|
||||
@@ -167,12 +167,12 @@
|
||||
"peerDependencies": {
|
||||
"@cornerstonejs/codec-charls": "1.2.3",
|
||||
"@cornerstonejs/codec-libjpeg-turbo-8bit": "1.2.2",
|
||||
"@cornerstonejs/codec-openjpeg": "1.2.4",
|
||||
"@cornerstonejs/codec-openjpeg": "1.3.0",
|
||||
"@cornerstonejs/codec-openjph": "2.4.7",
|
||||
"@cornerstonejs/dicom-image-loader": "4.5.5",
|
||||
"@ohif/core": "3.12.0-beta.58",
|
||||
"@ohif/ui": "3.12.0-beta.58",
|
||||
"dcmjs": "0.43.1",
|
||||
"@cornerstonejs/dicom-image-loader": "4.12.6",
|
||||
"@ohif/core": "3.12.0-beta.94",
|
||||
"@ohif/ui": "3.12.0-beta.94",
|
||||
"dcmjs": "0.45.0",
|
||||
"dicom-parser": "1.8.21",
|
||||
"hammerjs": "2.0.8",
|
||||
"prop-types": "15.8.1",
|
||||
@@ -183,19 +183,19 @@
|
||||
},
|
||||
"extensions/cornerstone-dicom-pmap": {
|
||||
"name": "@ohif/extension-cornerstone-dicom-pmap",
|
||||
"version": "3.12.0-beta.58",
|
||||
"version": "3.12.0-beta.94",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "7.28.2",
|
||||
"@cornerstonejs/adapters": "4.5.5",
|
||||
"@cornerstonejs/core": "4.5.5",
|
||||
"@kitware/vtk.js": "32.12.0",
|
||||
"@cornerstonejs/adapters": "4.12.6",
|
||||
"@cornerstonejs/core": "4.12.6",
|
||||
"@kitware/vtk.js": "34.15.1",
|
||||
"react-color": "2.19.3",
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@ohif/core": "3.12.0-beta.56",
|
||||
"@ohif/extension-cornerstone": "3.12.0-beta.56",
|
||||
"@ohif/extension-default": "3.12.0-beta.56",
|
||||
"@ohif/i18n": "3.12.0-beta.56",
|
||||
"@ohif/core": "3.12.0-beta.93",
|
||||
"@ohif/extension-cornerstone": "3.12.0-beta.93",
|
||||
"@ohif/extension-default": "3.12.0-beta.93",
|
||||
"@ohif/i18n": "3.12.0-beta.93",
|
||||
"prop-types": "15.8.1",
|
||||
"react": "18.3.1",
|
||||
"react-dom": "18.3.1",
|
||||
@@ -206,16 +206,16 @@
|
||||
},
|
||||
"extensions/cornerstone-dicom-rt": {
|
||||
"name": "@ohif/extension-cornerstone-dicom-rt",
|
||||
"version": "3.12.0-beta.58",
|
||||
"version": "3.12.0-beta.94",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "7.28.2",
|
||||
"react-color": "2.19.3",
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@ohif/core": "3.12.0-beta.56",
|
||||
"@ohif/extension-cornerstone": "3.12.0-beta.56",
|
||||
"@ohif/extension-default": "3.12.0-beta.56",
|
||||
"@ohif/i18n": "3.12.0-beta.56",
|
||||
"@ohif/core": "3.12.0-beta.93",
|
||||
"@ohif/extension-cornerstone": "3.12.0-beta.93",
|
||||
"@ohif/extension-default": "3.12.0-beta.93",
|
||||
"@ohif/i18n": "3.12.0-beta.93",
|
||||
"prop-types": "15.8.1",
|
||||
"react": "18.3.1",
|
||||
"react-dom": "18.3.1",
|
||||
@@ -226,19 +226,19 @@
|
||||
},
|
||||
"extensions/cornerstone-dicom-seg": {
|
||||
"name": "@ohif/extension-cornerstone-dicom-seg",
|
||||
"version": "3.12.0-beta.58",
|
||||
"version": "3.12.0-beta.94",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "7.28.2",
|
||||
"@cornerstonejs/adapters": "4.5.5",
|
||||
"@cornerstonejs/core": "4.5.5",
|
||||
"@kitware/vtk.js": "32.12.0",
|
||||
"@cornerstonejs/adapters": "4.12.6",
|
||||
"@cornerstonejs/core": "4.12.6",
|
||||
"@kitware/vtk.js": "34.15.1",
|
||||
"react-color": "2.19.3",
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@ohif/core": "3.12.0-beta.56",
|
||||
"@ohif/extension-cornerstone": "3.12.0-beta.56",
|
||||
"@ohif/extension-default": "3.12.0-beta.56",
|
||||
"@ohif/i18n": "3.12.0-beta.56",
|
||||
"@ohif/core": "3.12.0-beta.93",
|
||||
"@ohif/extension-cornerstone": "3.12.0-beta.93",
|
||||
"@ohif/extension-default": "3.12.0-beta.93",
|
||||
"@ohif/i18n": "3.12.0-beta.93",
|
||||
"prop-types": "15.8.1",
|
||||
"react": "18.3.1",
|
||||
"react-dom": "18.3.1",
|
||||
@@ -249,20 +249,20 @@
|
||||
},
|
||||
"extensions/cornerstone-dicom-sr": {
|
||||
"name": "@ohif/extension-cornerstone-dicom-sr",
|
||||
"version": "3.12.0-beta.58",
|
||||
"version": "3.12.0-beta.94",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "7.28.2",
|
||||
"@cornerstonejs/adapters": "4.5.5",
|
||||
"@cornerstonejs/core": "4.5.5",
|
||||
"@cornerstonejs/tools": "4.5.5",
|
||||
"@cornerstonejs/adapters": "4.12.6",
|
||||
"@cornerstonejs/core": "4.12.6",
|
||||
"@cornerstonejs/tools": "4.12.6",
|
||||
"classnames": "2.5.1",
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@ohif/core": "3.12.0-beta.58",
|
||||
"@ohif/extension-cornerstone": "3.12.0-beta.58",
|
||||
"@ohif/extension-measurement-tracking": "3.12.0-beta.58",
|
||||
"@ohif/ui": "3.12.0-beta.58",
|
||||
"dcmjs": "0.43.1",
|
||||
"@ohif/core": "3.12.0-beta.94",
|
||||
"@ohif/extension-cornerstone": "3.12.0-beta.94",
|
||||
"@ohif/extension-measurement-tracking": "3.12.0-beta.94",
|
||||
"@ohif/ui": "3.12.0-beta.94",
|
||||
"dcmjs": "0.45.0",
|
||||
"dicom-parser": "1.8.21",
|
||||
"hammerjs": "2.0.8",
|
||||
"prop-types": "15.8.1",
|
||||
@@ -271,20 +271,20 @@
|
||||
},
|
||||
"extensions/cornerstone-dynamic-volume": {
|
||||
"name": "@ohif/extension-cornerstone-dynamic-volume",
|
||||
"version": "3.12.0-beta.58",
|
||||
"version": "3.12.0-beta.94",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "7.28.2",
|
||||
"@cornerstonejs/core": "4.5.5",
|
||||
"@cornerstonejs/tools": "4.5.5",
|
||||
"@cornerstonejs/core": "4.12.6",
|
||||
"@cornerstonejs/tools": "4.12.6",
|
||||
"classnames": "2.5.1",
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@ohif/core": "3.12.0-beta.56",
|
||||
"@ohif/extension-cornerstone": "3.12.0-beta.56",
|
||||
"@ohif/extension-default": "3.12.0-beta.56",
|
||||
"@ohif/i18n": "3.12.0-beta.56",
|
||||
"@ohif/ui": "3.12.0-beta.56",
|
||||
"dcmjs": "0.43.1",
|
||||
"@ohif/core": "3.12.0-beta.93",
|
||||
"@ohif/extension-cornerstone": "3.12.0-beta.93",
|
||||
"@ohif/extension-default": "3.12.0-beta.93",
|
||||
"@ohif/i18n": "3.12.0-beta.93",
|
||||
"@ohif/ui": "3.12.0-beta.93",
|
||||
"dcmjs": "0.45.0",
|
||||
"dicom-parser": "1.8.21",
|
||||
"hammerjs": "2.0.8",
|
||||
"prop-types": "15.8.1",
|
||||
@@ -293,7 +293,7 @@
|
||||
},
|
||||
"extensions/default": {
|
||||
"name": "@ohif/extension-default",
|
||||
"version": "3.12.0-beta.58",
|
||||
"version": "3.12.0-beta.94",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "7.28.2",
|
||||
"@cornerstonejs/calculate-suv": "1.1.0",
|
||||
@@ -301,9 +301,9 @@
|
||||
"lodash.uniqby": "4.7.0",
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@ohif/core": "3.12.0-beta.58",
|
||||
"@ohif/i18n": "3.12.0-beta.58",
|
||||
"dcmjs": "0.43.1",
|
||||
"@ohif/core": "3.12.0-beta.94",
|
||||
"@ohif/i18n": "3.12.0-beta.94",
|
||||
"dcmjs": "0.45.0",
|
||||
"dicomweb-client": "0.10.4",
|
||||
"prop-types": "15.8.1",
|
||||
"react": "18.3.1",
|
||||
@@ -316,21 +316,21 @@
|
||||
},
|
||||
"extensions/dicom-microscopy": {
|
||||
"name": "@ohif/extension-dicom-microscopy",
|
||||
"version": "3.12.0-beta.58",
|
||||
"version": "3.12.0-beta.94",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "7.28.2",
|
||||
"@cornerstonejs/codec-charls": "1.2.3",
|
||||
"@cornerstonejs/codec-libjpeg-turbo-8bit": "1.2.2",
|
||||
"@cornerstonejs/codec-openjpeg": "1.2.4",
|
||||
"@cornerstonejs/codec-openjpeg": "1.3.0",
|
||||
"colormap": "2.3.2",
|
||||
"lodash.debounce": "4.0.8",
|
||||
"mathjs": "12.4.3",
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@ohif/core": "3.12.0-beta.56",
|
||||
"@ohif/extension-default": "3.12.0-beta.56",
|
||||
"@ohif/i18n": "3.12.0-beta.56",
|
||||
"@ohif/ui": "3.12.0-beta.56",
|
||||
"@ohif/core": "3.12.0-beta.93",
|
||||
"@ohif/extension-default": "3.12.0-beta.93",
|
||||
"@ohif/i18n": "3.12.0-beta.93",
|
||||
"@ohif/ui": "3.12.0-beta.93",
|
||||
"prop-types": "15.8.1",
|
||||
"react": "18.3.1",
|
||||
"react-dom": "18.3.1",
|
||||
@@ -341,15 +341,15 @@
|
||||
},
|
||||
"extensions/dicom-pdf": {
|
||||
"name": "@ohif/extension-dicom-pdf",
|
||||
"version": "3.12.0-beta.58",
|
||||
"version": "3.12.0-beta.94",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "7.28.2",
|
||||
"classnames": "2.5.1",
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@ohif/core": "3.12.0-beta.56",
|
||||
"@ohif/ui": "3.12.0-beta.56",
|
||||
"dcmjs": "0.43.1",
|
||||
"@ohif/core": "3.12.0-beta.93",
|
||||
"@ohif/ui": "3.12.0-beta.93",
|
||||
"dcmjs": "0.45.0",
|
||||
"dicom-parser": "1.8.21",
|
||||
"hammerjs": "2.0.8",
|
||||
"prop-types": "15.8.1",
|
||||
@@ -358,15 +358,15 @@
|
||||
},
|
||||
"extensions/dicom-video": {
|
||||
"name": "@ohif/extension-dicom-video",
|
||||
"version": "3.12.0-beta.58",
|
||||
"version": "3.12.0-beta.94",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "7.28.2",
|
||||
"classnames": "2.5.1",
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@ohif/core": "3.12.0-beta.56",
|
||||
"@ohif/ui": "3.12.0-beta.56",
|
||||
"dcmjs": "0.43.1",
|
||||
"@ohif/core": "3.12.0-beta.93",
|
||||
"@ohif/ui": "3.12.0-beta.93",
|
||||
"dcmjs": "0.45.0",
|
||||
"dicom-parser": "1.8.21",
|
||||
"hammerjs": "2.0.8",
|
||||
"prop-types": "15.8.1",
|
||||
@@ -375,22 +375,22 @@
|
||||
},
|
||||
"extensions/measurement-tracking": {
|
||||
"name": "@ohif/extension-measurement-tracking",
|
||||
"version": "3.12.0-beta.58",
|
||||
"version": "3.12.0-beta.94",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "7.28.2",
|
||||
"@ohif/ui": "3.12.0-beta.58",
|
||||
"@ohif/ui": "3.12.0-beta.94",
|
||||
"@xstate/react": "3.2.2",
|
||||
"xstate": "4.38.3",
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@cornerstonejs/core": "4.5.5",
|
||||
"@cornerstonejs/tools": "4.5.5",
|
||||
"@ohif/core": "3.12.0-beta.58",
|
||||
"@ohif/extension-cornerstone-dicom-sr": "3.12.0-beta.58",
|
||||
"@ohif/extension-default": "3.12.0-beta.58",
|
||||
"@ohif/ui": "3.12.0-beta.58",
|
||||
"@cornerstonejs/core": "4.12.6",
|
||||
"@cornerstonejs/tools": "4.12.6",
|
||||
"@ohif/core": "3.12.0-beta.94",
|
||||
"@ohif/extension-cornerstone-dicom-sr": "3.12.0-beta.94",
|
||||
"@ohif/extension-default": "3.12.0-beta.94",
|
||||
"@ohif/ui": "3.12.0-beta.94",
|
||||
"classnames": "2.5.1",
|
||||
"dcmjs": "0.43.1",
|
||||
"dcmjs": "0.45.0",
|
||||
"lodash.debounce": "4.0.8",
|
||||
"prop-types": "15.8.1",
|
||||
"react": "18.3.1",
|
||||
@@ -401,15 +401,15 @@
|
||||
},
|
||||
"extensions/test-extension": {
|
||||
"name": "@ohif/extension-test",
|
||||
"version": "3.12.0-beta.58",
|
||||
"version": "3.12.0-beta.94",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "7.28.2",
|
||||
"classnames": "2.5.1",
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@ohif/core": "3.12.0-beta.56",
|
||||
"@ohif/ui": "3.12.0-beta.56",
|
||||
"dcmjs": "0.43.1",
|
||||
"@ohif/core": "3.12.0-beta.93",
|
||||
"@ohif/ui": "3.12.0-beta.93",
|
||||
"dcmjs": "0.45.0",
|
||||
"dicom-parser": "1.8.21",
|
||||
"hammerjs": "2.0.8",
|
||||
"prop-types": "15.8.1",
|
||||
@@ -418,15 +418,15 @@
|
||||
},
|
||||
"extensions/tmtv": {
|
||||
"name": "@ohif/extension-tmtv",
|
||||
"version": "3.12.0-beta.58",
|
||||
"version": "3.12.0-beta.94",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "7.28.2",
|
||||
"classnames": "2.5.1",
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@ohif/core": "3.12.0-beta.56",
|
||||
"@ohif/ui": "3.12.0-beta.56",
|
||||
"dcmjs": "0.43.1",
|
||||
"@ohif/core": "3.12.0-beta.93",
|
||||
"@ohif/ui": "3.12.0-beta.93",
|
||||
"dcmjs": "0.45.0",
|
||||
"dicom-parser": "1.8.21",
|
||||
"hammerjs": "2.0.8",
|
||||
"prop-types": "15.8.1",
|
||||
@@ -435,16 +435,16 @@
|
||||
},
|
||||
"extensions/usAnnotation": {
|
||||
"name": "@ohif/extension-ultrasound-pleura-bline",
|
||||
"version": "3.12.0-beta.58",
|
||||
"version": "3.12.0-beta.94",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "7.28.2",
|
||||
"@cornerstonejs/core": "4.5.5",
|
||||
"@cornerstonejs/tools": "4.5.5",
|
||||
"@ohif/core": "3.12.0-beta.56",
|
||||
"@ohif/extension-cornerstone": "3.12.0-beta.56",
|
||||
"@ohif/extension-default": "3.12.0-beta.56",
|
||||
"@ohif/i18n": "3.12.0-beta.56",
|
||||
"@ohif/ui-next": "3.12.0-beta.56",
|
||||
"@cornerstonejs/core": "4.12.6",
|
||||
"@cornerstonejs/tools": "4.12.6",
|
||||
"@ohif/core": "3.12.0-beta.93",
|
||||
"@ohif/extension-cornerstone": "3.12.0-beta.93",
|
||||
"@ohif/extension-default": "3.12.0-beta.93",
|
||||
"@ohif/i18n": "3.12.0-beta.93",
|
||||
"@ohif/ui-next": "3.12.0-beta.93",
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "7.28.0",
|
||||
@@ -487,7 +487,7 @@
|
||||
},
|
||||
"modes/basic": {
|
||||
"name": "@ohif/mode-basic",
|
||||
"version": "3.12.0-beta.58",
|
||||
"version": "3.12.0-beta.94",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "7.28.2",
|
||||
},
|
||||
@@ -496,19 +496,19 @@
|
||||
"webpack-merge": "5.10.0",
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@ohif/core": "3.12.0-beta.56",
|
||||
"@ohif/extension-cornerstone": "3.12.0-beta.56",
|
||||
"@ohif/extension-cornerstone-dicom-rt": "3.12.0-beta.56",
|
||||
"@ohif/extension-cornerstone-dicom-seg": "3.12.0-beta.56",
|
||||
"@ohif/extension-cornerstone-dicom-sr": "3.12.0-beta.56",
|
||||
"@ohif/extension-default": "3.12.0-beta.56",
|
||||
"@ohif/extension-dicom-pdf": "3.12.0-beta.56",
|
||||
"@ohif/extension-dicom-video": "3.12.0-beta.56",
|
||||
"@ohif/core": "3.12.0-beta.93",
|
||||
"@ohif/extension-cornerstone": "3.12.0-beta.93",
|
||||
"@ohif/extension-cornerstone-dicom-rt": "3.12.0-beta.93",
|
||||
"@ohif/extension-cornerstone-dicom-seg": "3.12.0-beta.93",
|
||||
"@ohif/extension-cornerstone-dicom-sr": "3.12.0-beta.93",
|
||||
"@ohif/extension-default": "3.12.0-beta.93",
|
||||
"@ohif/extension-dicom-pdf": "3.12.0-beta.93",
|
||||
"@ohif/extension-dicom-video": "3.12.0-beta.93",
|
||||
},
|
||||
},
|
||||
"modes/basic-dev-mode": {
|
||||
"name": "@ohif/mode-basic-dev-mode",
|
||||
"version": "3.12.0-beta.58",
|
||||
"version": "3.12.0-beta.94",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "7.28.2",
|
||||
"i18next": "17.3.1",
|
||||
@@ -518,17 +518,17 @@
|
||||
"webpack-merge": "5.10.0",
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@ohif/core": "3.12.0-beta.56",
|
||||
"@ohif/extension-cornerstone": "3.12.0-beta.56",
|
||||
"@ohif/extension-cornerstone-dicom-sr": "3.12.0-beta.56",
|
||||
"@ohif/extension-default": "3.12.0-beta.56",
|
||||
"@ohif/extension-dicom-pdf": "3.12.0-beta.56",
|
||||
"@ohif/extension-dicom-video": "3.12.0-beta.56",
|
||||
"@ohif/core": "3.12.0-beta.93",
|
||||
"@ohif/extension-cornerstone": "3.12.0-beta.93",
|
||||
"@ohif/extension-cornerstone-dicom-sr": "3.12.0-beta.93",
|
||||
"@ohif/extension-default": "3.12.0-beta.93",
|
||||
"@ohif/extension-dicom-pdf": "3.12.0-beta.93",
|
||||
"@ohif/extension-dicom-video": "3.12.0-beta.93",
|
||||
},
|
||||
},
|
||||
"modes/basic-test-mode": {
|
||||
"name": "@ohif/mode-test",
|
||||
"version": "3.12.0-beta.58",
|
||||
"version": "3.12.0-beta.94",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "7.28.2",
|
||||
"i18next": "17.3.1",
|
||||
@@ -538,19 +538,19 @@
|
||||
"webpack-merge": "5.10.0",
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@ohif/core": "3.12.0-beta.56",
|
||||
"@ohif/extension-cornerstone": "3.12.0-beta.56",
|
||||
"@ohif/extension-cornerstone-dicom-sr": "3.12.0-beta.56",
|
||||
"@ohif/extension-default": "3.12.0-beta.56",
|
||||
"@ohif/extension-dicom-pdf": "3.12.0-beta.56",
|
||||
"@ohif/extension-dicom-video": "3.12.0-beta.56",
|
||||
"@ohif/extension-measurement-tracking": "3.12.0-beta.56",
|
||||
"@ohif/extension-test": "3.12.0-beta.56",
|
||||
"@ohif/core": "3.12.0-beta.93",
|
||||
"@ohif/extension-cornerstone": "3.12.0-beta.93",
|
||||
"@ohif/extension-cornerstone-dicom-sr": "3.12.0-beta.93",
|
||||
"@ohif/extension-default": "3.12.0-beta.93",
|
||||
"@ohif/extension-dicom-pdf": "3.12.0-beta.93",
|
||||
"@ohif/extension-dicom-video": "3.12.0-beta.93",
|
||||
"@ohif/extension-measurement-tracking": "3.12.0-beta.93",
|
||||
"@ohif/extension-test": "3.12.0-beta.93",
|
||||
},
|
||||
},
|
||||
"modes/longitudinal": {
|
||||
"name": "@ohif/mode-longitudinal",
|
||||
"version": "3.12.0-beta.58",
|
||||
"version": "3.12.0-beta.94",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "7.28.2",
|
||||
"i18next": "17.3.1",
|
||||
@@ -560,32 +560,33 @@
|
||||
"webpack-merge": "5.10.0",
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@ohif/core": "3.12.0-beta.56",
|
||||
"@ohif/extension-cornerstone": "3.12.0-beta.56",
|
||||
"@ohif/extension-cornerstone-dicom-rt": "3.12.0-beta.56",
|
||||
"@ohif/extension-cornerstone-dicom-seg": "3.12.0-beta.56",
|
||||
"@ohif/extension-cornerstone-dicom-sr": "3.12.0-beta.56",
|
||||
"@ohif/extension-default": "3.12.0-beta.56",
|
||||
"@ohif/extension-dicom-pdf": "3.12.0-beta.56",
|
||||
"@ohif/extension-dicom-video": "3.12.0-beta.56",
|
||||
"@ohif/extension-measurement-tracking": "3.12.0-beta.56",
|
||||
"@ohif/core": "3.12.0-beta.93",
|
||||
"@ohif/extension-cornerstone": "3.12.0-beta.93",
|
||||
"@ohif/extension-cornerstone-dicom-rt": "3.12.0-beta.93",
|
||||
"@ohif/extension-cornerstone-dicom-seg": "3.12.0-beta.93",
|
||||
"@ohif/extension-cornerstone-dicom-sr": "3.12.0-beta.93",
|
||||
"@ohif/extension-default": "3.12.0-beta.93",
|
||||
"@ohif/extension-dicom-pdf": "3.12.0-beta.93",
|
||||
"@ohif/extension-dicom-video": "3.12.0-beta.93",
|
||||
"@ohif/extension-measurement-tracking": "3.12.0-beta.93",
|
||||
"@ohif/mode-basic": "3.12.0-beta.93",
|
||||
},
|
||||
},
|
||||
"modes/microscopy": {
|
||||
"name": "@ohif/mode-microscopy",
|
||||
"version": "3.12.0-beta.58",
|
||||
"version": "3.12.0-beta.94",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "7.28.2",
|
||||
"i18next": "17.3.1",
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@ohif/core": "3.12.0-beta.56",
|
||||
"@ohif/extension-dicom-microscopy": "3.12.0-beta.56",
|
||||
"@ohif/core": "3.12.0-beta.93",
|
||||
"@ohif/extension-dicom-microscopy": "3.12.0-beta.93",
|
||||
},
|
||||
},
|
||||
"modes/preclinical-4d": {
|
||||
"name": "@ohif/mode-preclinical-4d",
|
||||
"version": "3.12.0-beta.58",
|
||||
"version": "3.12.0-beta.94",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "7.28.2",
|
||||
},
|
||||
@@ -594,17 +595,17 @@
|
||||
"webpack-merge": "5.10.0",
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@ohif/core": "3.12.0-beta.56",
|
||||
"@ohif/extension-cornerstone": "3.12.0-beta.56",
|
||||
"@ohif/extension-cornerstone-dicom-seg": "3.12.0-beta.56",
|
||||
"@ohif/extension-cornerstone-dynamic-volume": "3.12.0-beta.56",
|
||||
"@ohif/extension-default": "3.12.0-beta.56",
|
||||
"@ohif/extension-tmtv": "3.12.0-beta.56",
|
||||
"@ohif/core": "3.12.0-beta.93",
|
||||
"@ohif/extension-cornerstone": "3.12.0-beta.93",
|
||||
"@ohif/extension-cornerstone-dicom-seg": "3.12.0-beta.93",
|
||||
"@ohif/extension-cornerstone-dynamic-volume": "3.12.0-beta.93",
|
||||
"@ohif/extension-default": "3.12.0-beta.93",
|
||||
"@ohif/extension-tmtv": "3.12.0-beta.93",
|
||||
},
|
||||
},
|
||||
"modes/segmentation": {
|
||||
"name": "@ohif/mode-segmentation",
|
||||
"version": "3.12.0-beta.58",
|
||||
"version": "3.12.0-beta.94",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "7.28.2",
|
||||
"i18next": "17.3.1",
|
||||
@@ -636,19 +637,20 @@
|
||||
"webpack-merge": "5.10.0",
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@ohif/core": "3.12.0-beta.56",
|
||||
"@ohif/extension-cornerstone": "3.12.0-beta.56",
|
||||
"@ohif/extension-cornerstone-dicom-rt": "3.12.0-beta.56",
|
||||
"@ohif/extension-cornerstone-dicom-seg": "3.12.0-beta.56",
|
||||
"@ohif/extension-cornerstone-dicom-sr": "3.12.0-beta.56",
|
||||
"@ohif/extension-default": "3.12.0-beta.56",
|
||||
"@ohif/extension-dicom-pdf": "3.12.0-beta.56",
|
||||
"@ohif/extension-dicom-video": "3.12.0-beta.56",
|
||||
"@ohif/core": "3.12.0-beta.93",
|
||||
"@ohif/extension-cornerstone": "3.12.0-beta.93",
|
||||
"@ohif/extension-cornerstone-dicom-rt": "3.12.0-beta.93",
|
||||
"@ohif/extension-cornerstone-dicom-seg": "3.12.0-beta.93",
|
||||
"@ohif/extension-cornerstone-dicom-sr": "3.12.0-beta.93",
|
||||
"@ohif/extension-default": "3.12.0-beta.93",
|
||||
"@ohif/extension-dicom-pdf": "3.12.0-beta.93",
|
||||
"@ohif/extension-dicom-video": "3.12.0-beta.93",
|
||||
"@ohif/mode-basic": "3.12.0-beta.93",
|
||||
},
|
||||
},
|
||||
"modes/tmtv": {
|
||||
"name": "@ohif/mode-tmtv",
|
||||
"version": "3.12.0-beta.58",
|
||||
"version": "3.12.0-beta.94",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "7.28.2",
|
||||
"i18next": "17.3.1",
|
||||
@@ -658,25 +660,25 @@
|
||||
"webpack-merge": "5.10.0",
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@ohif/core": "3.12.0-beta.56",
|
||||
"@ohif/extension-cornerstone": "3.12.0-beta.56",
|
||||
"@ohif/extension-cornerstone-dicom-sr": "3.12.0-beta.56",
|
||||
"@ohif/extension-default": "3.12.0-beta.56",
|
||||
"@ohif/extension-dicom-pdf": "3.12.0-beta.56",
|
||||
"@ohif/extension-dicom-video": "3.12.0-beta.56",
|
||||
"@ohif/extension-measurement-tracking": "3.12.0-beta.56",
|
||||
"@ohif/core": "3.12.0-beta.93",
|
||||
"@ohif/extension-cornerstone": "3.12.0-beta.93",
|
||||
"@ohif/extension-cornerstone-dicom-sr": "3.12.0-beta.93",
|
||||
"@ohif/extension-default": "3.12.0-beta.93",
|
||||
"@ohif/extension-dicom-pdf": "3.12.0-beta.93",
|
||||
"@ohif/extension-dicom-video": "3.12.0-beta.93",
|
||||
"@ohif/extension-measurement-tracking": "3.12.0-beta.93",
|
||||
},
|
||||
},
|
||||
"modes/usAnnotation": {
|
||||
"name": "@ohif/mode-ultrasound-pleura-bline",
|
||||
"version": "3.12.0-beta.58",
|
||||
"version": "3.12.0-beta.94",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "7.28.2",
|
||||
"@cornerstonejs/core": "4.5.5",
|
||||
"@cornerstonejs/tools": "4.5.5",
|
||||
"@ohif/core": "3.12.0-beta.56",
|
||||
"@ohif/extension-cornerstone-dicom-sr": "3.12.0-beta.56",
|
||||
"@ohif/extension-ultrasound-pleura-bline": "3.12.0-beta.56",
|
||||
"@cornerstonejs/core": "4.12.6",
|
||||
"@cornerstonejs/tools": "4.12.6",
|
||||
"@ohif/core": "3.12.0-beta.93",
|
||||
"@ohif/extension-cornerstone-dicom-sr": "3.12.0-beta.93",
|
||||
"@ohif/extension-ultrasound-pleura-bline": "3.12.0-beta.93",
|
||||
"i18next": "17.3.1",
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -708,40 +710,40 @@
|
||||
},
|
||||
"platform/app": {
|
||||
"name": "@ohif/app",
|
||||
"version": "3.12.0-beta.58",
|
||||
"version": "3.12.0-beta.94",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "7.28.2",
|
||||
"@cornerstonejs/codec-charls": "1.2.3",
|
||||
"@cornerstonejs/codec-libjpeg-turbo-8bit": "1.2.2",
|
||||
"@cornerstonejs/codec-openjpeg": "1.2.4",
|
||||
"@cornerstonejs/codec-openjpeg": "1.3.0",
|
||||
"@cornerstonejs/codec-openjph": "2.4.7",
|
||||
"@cornerstonejs/dicom-image-loader": "4.5.5",
|
||||
"@cornerstonejs/dicom-image-loader": "4.12.6",
|
||||
"@emotion/serialize": "1.3.3",
|
||||
"@ohif/core": "3.12.0-beta.56",
|
||||
"@ohif/extension-cornerstone": "3.12.0-beta.56",
|
||||
"@ohif/extension-cornerstone-dicom-rt": "3.12.0-beta.56",
|
||||
"@ohif/extension-cornerstone-dicom-seg": "3.12.0-beta.56",
|
||||
"@ohif/extension-cornerstone-dicom-sr": "3.12.0-beta.56",
|
||||
"@ohif/extension-default": "3.12.0-beta.56",
|
||||
"@ohif/extension-dicom-microscopy": "3.12.0-beta.56",
|
||||
"@ohif/extension-dicom-pdf": "3.12.0-beta.56",
|
||||
"@ohif/extension-dicom-video": "3.12.0-beta.56",
|
||||
"@ohif/extension-test": "3.12.0-beta.56",
|
||||
"@ohif/extension-ultrasound-pleura-bline": "3.12.0-beta.56",
|
||||
"@ohif/i18n": "3.12.0-beta.56",
|
||||
"@ohif/mode-basic-dev-mode": "3.12.0-beta.56",
|
||||
"@ohif/mode-longitudinal": "3.12.0-beta.56",
|
||||
"@ohif/mode-microscopy": "3.12.0-beta.56",
|
||||
"@ohif/mode-test": "3.12.0-beta.56",
|
||||
"@ohif/mode-ultrasound-pleura-bline": "3.12.0-beta.56",
|
||||
"@ohif/ui": "3.12.0-beta.56",
|
||||
"@ohif/ui-next": "3.12.0-beta.56",
|
||||
"@ohif/core": "3.12.0-beta.93",
|
||||
"@ohif/extension-cornerstone": "3.12.0-beta.93",
|
||||
"@ohif/extension-cornerstone-dicom-rt": "3.12.0-beta.93",
|
||||
"@ohif/extension-cornerstone-dicom-seg": "3.12.0-beta.93",
|
||||
"@ohif/extension-cornerstone-dicom-sr": "3.12.0-beta.93",
|
||||
"@ohif/extension-default": "3.12.0-beta.93",
|
||||
"@ohif/extension-dicom-microscopy": "3.12.0-beta.93",
|
||||
"@ohif/extension-dicom-pdf": "3.12.0-beta.93",
|
||||
"@ohif/extension-dicom-video": "3.12.0-beta.93",
|
||||
"@ohif/extension-test": "3.12.0-beta.93",
|
||||
"@ohif/extension-ultrasound-pleura-bline": "3.12.0-beta.93",
|
||||
"@ohif/i18n": "3.12.0-beta.93",
|
||||
"@ohif/mode-basic-dev-mode": "3.12.0-beta.93",
|
||||
"@ohif/mode-longitudinal": "3.12.0-beta.93",
|
||||
"@ohif/mode-microscopy": "3.12.0-beta.93",
|
||||
"@ohif/mode-test": "3.12.0-beta.93",
|
||||
"@ohif/mode-ultrasound-pleura-bline": "3.12.0-beta.93",
|
||||
"@ohif/ui": "3.12.0-beta.93",
|
||||
"@ohif/ui-next": "3.12.0-beta.93",
|
||||
"@svgr/webpack": "8.1.0",
|
||||
"@types/react": "18.3.23",
|
||||
"classnames": "2.5.1",
|
||||
"core-js": "3.45.1",
|
||||
"cornerstone-math": "0.1.10",
|
||||
"dcmjs": "0.43.1",
|
||||
"dcmjs": "0.45.0",
|
||||
"detect-gpu": "4.0.50",
|
||||
"dicom-parser": "1.8.21",
|
||||
"dotenv-webpack": "1.8.0",
|
||||
@@ -787,7 +789,7 @@
|
||||
},
|
||||
"platform/cli": {
|
||||
"name": "@ohif/cli",
|
||||
"version": "3.12.0-beta.58",
|
||||
"version": "3.12.0-beta.94",
|
||||
"bin": {
|
||||
"ohif-cli": "src/index.js",
|
||||
},
|
||||
@@ -811,10 +813,10 @@
|
||||
},
|
||||
"platform/core": {
|
||||
"name": "@ohif/core",
|
||||
"version": "3.12.0-beta.58",
|
||||
"version": "3.12.0-beta.94",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "7.28.2",
|
||||
"dcmjs": "0.43.1",
|
||||
"dcmjs": "0.45.0",
|
||||
"dicomweb-client": "0.10.4",
|
||||
"gl-matrix": "3.4.3",
|
||||
"immutability-helper": "3.1.1",
|
||||
@@ -834,18 +836,18 @@
|
||||
"peerDependencies": {
|
||||
"@cornerstonejs/codec-charls": "1.2.3",
|
||||
"@cornerstonejs/codec-libjpeg-turbo-8bit": "1.2.2",
|
||||
"@cornerstonejs/codec-openjpeg": "1.2.4",
|
||||
"@cornerstonejs/codec-openjpeg": "1.3.0",
|
||||
"@cornerstonejs/codec-openjph": "2.4.7",
|
||||
"@cornerstonejs/core": "4.5.5",
|
||||
"@cornerstonejs/dicom-image-loader": "4.5.5",
|
||||
"@ohif/ui": "3.12.0-beta.58",
|
||||
"@cornerstonejs/core": "4.12.6",
|
||||
"@cornerstonejs/dicom-image-loader": "4.12.6",
|
||||
"@ohif/ui": "3.12.0-beta.94",
|
||||
"cornerstone-math": "0.1.9",
|
||||
"dicom-parser": "1.8.21",
|
||||
},
|
||||
},
|
||||
"platform/i18n": {
|
||||
"name": "@ohif/i18n",
|
||||
"version": "3.12.0-beta.58",
|
||||
"version": "3.12.0-beta.94",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "7.28.2",
|
||||
"i18next-locize-backend": "2.2.2",
|
||||
@@ -870,7 +872,7 @@
|
||||
},
|
||||
"platform/ui": {
|
||||
"name": "@ohif/ui",
|
||||
"version": "3.12.0-beta.58",
|
||||
"version": "3.12.0-beta.94",
|
||||
"dependencies": {
|
||||
"@testing-library/react": "13.4.0",
|
||||
"browser-detect": "0.2.28",
|
||||
@@ -921,7 +923,7 @@
|
||||
},
|
||||
"platform/ui-next": {
|
||||
"name": "@ohif/ui-next",
|
||||
"version": "3.12.0-beta.58",
|
||||
"version": "3.12.0-beta.94",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-accordion": "1.2.11",
|
||||
"@radix-ui/react-checkbox": "1.3.2",
|
||||
@@ -966,15 +968,16 @@
|
||||
},
|
||||
"overrides": {
|
||||
"@babel/runtime-corejs2": "7.26.10",
|
||||
"@cornerstonejs/codec-openjpeg": "1.3.0",
|
||||
"axios": "1.12.0",
|
||||
"body-parser": "1.20.3",
|
||||
"commander": "8.3.0",
|
||||
"core-js": "3.45.1",
|
||||
"cross-env": "7.0.3",
|
||||
"cross-spawn": "7.0.6",
|
||||
"dcmjs": "0.43.1",
|
||||
"form-data": ">=4.0.4",
|
||||
"dcmjs": "0.45.0",
|
||||
"glob-parent": "6.0.2",
|
||||
"node-forge": "1.3.2",
|
||||
"nth-check": "2.1.1",
|
||||
"package-json": "8.1.1",
|
||||
"path-to-regexp": "0.1.12",
|
||||
@@ -1228,9 +1231,9 @@
|
||||
|
||||
"@bcoe/v8-coverage": ["@bcoe/v8-coverage@0.2.3", "", {}, "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw=="],
|
||||
|
||||
"@cornerstonejs/adapters": ["@cornerstonejs/adapters@4.5.5", "", { "dependencies": { "@babel/runtime-corejs2": "7.26.10", "buffer": "6.0.3", "dcmjs": "0.43.1", "gl-matrix": "3.4.3", "ndarray": "1.0.19" }, "peerDependencies": { "@cornerstonejs/core": "4.5.5", "@cornerstonejs/tools": "4.5.5" } }, "sha512-tZC8ScqmSouWt5/0t9cUqtsSVW5ObhniyuZuF6QZvXOyWU7I4fkmtNX4RtTUi9cvuqoDA0nckejkEQhp5OjKEw=="],
|
||||
"@cornerstonejs/adapters": ["@cornerstonejs/adapters@4.12.6", "", { "dependencies": { "@babel/runtime-corejs2": "7.26.10", "buffer": "6.0.3", "dcmjs": "0.45.0", "gl-matrix": "3.4.3", "ndarray": "1.0.19" }, "peerDependencies": { "@cornerstonejs/core": "4.12.6", "@cornerstonejs/tools": "4.12.6" } }, "sha512-ukzrcNqmKK2iitrG5Zkn14ZawFFEL6QfrmEYZPb3rKJpOwb+CbEcGm3TLVlx+ugLavsZKXjB1mh1OSbLcn5SWw=="],
|
||||
|
||||
"@cornerstonejs/ai": ["@cornerstonejs/ai@4.5.5", "", { "dependencies": { "@babel/runtime-corejs2": "7.26.10", "buffer": "6.0.3", "dcmjs": "0.43.1", "gl-matrix": "3.4.3", "lodash.clonedeep": "4.5.0", "ndarray": "1.0.19", "onnxruntime-common": "1.17.1", "onnxruntime-web": "1.17.1" }, "peerDependencies": { "@cornerstonejs/core": "4.5.5", "@cornerstonejs/tools": "4.5.5" } }, "sha512-31/ARIIr2EyDwyeNsI58XLYpbEikaPqYuR/7tQMFvqwX0plpsaAZx0wQ+ta0YBag9/vAtpuyjg1sGMaqS7v9Yw=="],
|
||||
"@cornerstonejs/ai": ["@cornerstonejs/ai@4.12.6", "", { "dependencies": { "@babel/runtime-corejs2": "7.26.10", "buffer": "6.0.3", "dcmjs": "0.45.0", "gl-matrix": "3.4.3", "lodash.clonedeep": "4.5.0", "ndarray": "1.0.19", "onnxruntime-common": "1.17.1", "onnxruntime-web": "1.17.1" }, "peerDependencies": { "@cornerstonejs/core": "4.12.6", "@cornerstonejs/tools": "4.12.6" } }, "sha512-TA49MsMmrb/HY1b+axMQId3Wy91C7Di26hsZJrMHVyEyzN1IZgUMYBcs9DeNUCAVXUSCip4txtpbS+oNfX1dOw=="],
|
||||
|
||||
"@cornerstonejs/calculate-suv": ["@cornerstonejs/calculate-suv@1.1.0", "", {}, "sha512-Q9XraiDJif9aMFArD2iEuDO/HXbcRVCqB7KfaHgDrdTTjgDFovS91Psbdim7crypRSvE6dh/+HKeFNHdvNkA6w=="],
|
||||
|
||||
@@ -1238,19 +1241,19 @@
|
||||
|
||||
"@cornerstonejs/codec-libjpeg-turbo-8bit": ["@cornerstonejs/codec-libjpeg-turbo-8bit@1.2.2", "", {}, "sha512-aAUMK2958YNpOb/7G6e2/aG7hExTiFTASlMt/v90XA0pRHdWiNg5ny4S5SAju0FbIw4zcMnR0qfY+yW3VG2ivg=="],
|
||||
|
||||
"@cornerstonejs/codec-openjpeg": ["@cornerstonejs/codec-openjpeg@1.2.4", "", {}, "sha512-UT2su6xZZnCPSuWf2ldzKa/2+guQ7BGgfBSKqxanggwJHh48gZqIAzekmsLyJHMMK5YDK+ti+fzvVJhBS3Xi/g=="],
|
||||
"@cornerstonejs/codec-openjpeg": ["@cornerstonejs/codec-openjpeg@1.3.0", "", {}, "sha512-hP8WAZ63AcaDYmHbBVTY04x424AglXsRHrI6VBdW4eTiJ76f0heWrEodT9Sb3sNwazzYuyMOIZNndJPeVSeHcw=="],
|
||||
|
||||
"@cornerstonejs/codec-openjph": ["@cornerstonejs/codec-openjph@2.4.7", "", {}, "sha512-qvP4q4JDib7mi9r7LqKOwqz7YZ8gjtDX4ZCezeYf8+eb7MBXCz5uXAMeVF3yz9Axw4XiIMdB/pqXkm8tqCl13w=="],
|
||||
|
||||
"@cornerstonejs/core": ["@cornerstonejs/core@4.5.5", "", { "dependencies": { "@kitware/vtk.js": "32.12.1", "comlink": "4.4.2", "gl-matrix": "3.4.3", "loglevel": "1.9.2" } }, "sha512-fThmbuCEZosLUf46pNxAj0UiNYWpjKYNAj0pJoOLc6Ck3Q6lzsA4lF7Hv2LT9Ek0P9Hdfjwjjfjm4mkLevmZBg=="],
|
||||
"@cornerstonejs/core": ["@cornerstonejs/core@4.12.6", "", { "dependencies": { "@kitware/vtk.js": "34.15.1", "comlink": "4.4.2", "gl-matrix": "3.4.3", "loglevel": "1.9.2" } }, "sha512-JCwv4Alvjvbmh2lDSvGRUuWa809HL1GqhGL8zE3VctIpKYMcE+f0Ggd+RGScDWCnkIhrKzCHaSZdn7Z0zWzjWQ=="],
|
||||
|
||||
"@cornerstonejs/dicom-image-loader": ["@cornerstonejs/dicom-image-loader@4.5.5", "", { "dependencies": { "@cornerstonejs/codec-charls": "1.2.3", "@cornerstonejs/codec-libjpeg-turbo-8bit": "1.2.2", "@cornerstonejs/codec-openjpeg": "1.2.4", "@cornerstonejs/codec-openjph": "2.4.7", "comlink": "4.4.2", "dicom-parser": "1.8.21", "jpeg-lossless-decoder-js": "2.1.2", "pako": "2.1.0", "uuid": "9.0.1" }, "peerDependencies": { "@cornerstonejs/core": "4.5.5" } }, "sha512-v6nLkpS8rEFkNK8ROZkKQkiHDhOoGmVvknnKSuK0tWpN3Lpha9tfAfWiZyOJGV/HJcZhmXQcnbk3hR8/QUEs6w=="],
|
||||
"@cornerstonejs/dicom-image-loader": ["@cornerstonejs/dicom-image-loader@4.12.6", "", { "dependencies": { "@cornerstonejs/codec-charls": "1.2.3", "@cornerstonejs/codec-libjpeg-turbo-8bit": "1.2.2", "@cornerstonejs/codec-openjpeg": "1.3.0", "@cornerstonejs/codec-openjph": "2.4.7", "comlink": "4.4.2", "dicom-parser": "1.8.21", "jpeg-lossless-decoder-js": "2.1.2", "pako": "2.1.0", "uuid": "9.0.1" }, "peerDependencies": { "@cornerstonejs/core": "4.12.6" } }, "sha512-m94cWABepuYVaVNB5BY6YV0lkNVgwD1sDReJ1oubjk0bfZuAKQqM0AnpHLg0HLKf3TySTg3Nr1PbuI0yGw+eow=="],
|
||||
|
||||
"@cornerstonejs/labelmap-interpolation": ["@cornerstonejs/labelmap-interpolation@4.5.5", "", { "dependencies": { "@itk-wasm/morphological-contour-interpolation": "1.1.0", "itk-wasm": "1.0.0-b.165" }, "peerDependencies": { "@cornerstonejs/core": "4.5.5", "@cornerstonejs/tools": "4.5.5", "@kitware/vtk.js": "32.12.1" } }, "sha512-76z3uL1J/ASS2d0qBApDVq8JQ3S5mXMD+xZhfrrbqPCkk4X9d9UyLyjylxDgFjlE3QRL+l27/nAyn4yshW6wHA=="],
|
||||
"@cornerstonejs/labelmap-interpolation": ["@cornerstonejs/labelmap-interpolation@4.12.6", "", { "dependencies": { "@itk-wasm/morphological-contour-interpolation": "1.1.0", "itk-wasm": "1.0.0-b.165" }, "peerDependencies": { "@cornerstonejs/core": "4.12.6", "@cornerstonejs/tools": "4.12.6", "@kitware/vtk.js": "34.15.1" } }, "sha512-DeR+0B0ebZejNQ3P1uRYlijHH5cf6HJpZFVM8ZyDkTkgnusx7BCRHwjRnx/Tigp83KuaOz0SJNBJ+THQRfyXRw=="],
|
||||
|
||||
"@cornerstonejs/polymorphic-segmentation": ["@cornerstonejs/polymorphic-segmentation@4.5.5", "", { "dependencies": { "@icr/polyseg-wasm": "0.4.0" }, "peerDependencies": { "@cornerstonejs/core": "4.5.5", "@cornerstonejs/tools": "4.5.5", "@kitware/vtk.js": "32.12.1" } }, "sha512-KO6nM/NovzYz3txLKlpAciJDUpIQHI9cga1PpvnqtxGMxdLkXOkaKHeeN506qQD9MAGkTqpy2Z6pYflgRG9PYw=="],
|
||||
"@cornerstonejs/polymorphic-segmentation": ["@cornerstonejs/polymorphic-segmentation@4.12.6", "", { "dependencies": { "@icr/polyseg-wasm": "0.4.0" }, "peerDependencies": { "@cornerstonejs/core": "4.12.6", "@cornerstonejs/tools": "4.12.6", "@kitware/vtk.js": "34.15.1" } }, "sha512-gP8Ww7D9n+LKdylEPWQ4H4j73Wlnck8Gbj2KAPAjoo6EUSRlStQvs8+M8HHdscAF5RPBpyTqL/Jrz+TNlChY3A=="],
|
||||
|
||||
"@cornerstonejs/tools": ["@cornerstonejs/tools@4.5.5", "", { "dependencies": { "@types/offscreencanvas": "2019.7.3", "comlink": "4.4.2", "lodash.get": "4.4.2" }, "peerDependencies": { "@cornerstonejs/core": "4.5.5", "@kitware/vtk.js": "32.12.1", "@types/d3-array": "3.2.1", "@types/d3-interpolate": "3.0.4", "d3-array": "3.2.4", "d3-interpolate": "3.0.1", "gl-matrix": "3.4.3" } }, "sha512-IruCR6KWYM4Z1opnPod8bNkEtJnnQyfFXr0ilmRNDNHj6jQ1/uWQQX/Ss+eWnkAALCUYAU1Co/6zWL5Nuowp8A=="],
|
||||
"@cornerstonejs/tools": ["@cornerstonejs/tools@4.12.6", "", { "dependencies": { "@types/offscreencanvas": "2019.7.3", "comlink": "4.4.2", "lodash.get": "4.4.2" }, "peerDependencies": { "@cornerstonejs/core": "4.12.6", "@kitware/vtk.js": "34.15.1", "@types/d3-array": "3.2.1", "@types/d3-interpolate": "3.0.4", "d3-array": "3.2.4", "d3-interpolate": "3.0.1", "gl-matrix": "3.4.3" } }, "sha512-E9FWdsNrliYgaU7AS6ErEBVt/s6bL1DRLfGB7dAtrHBbcdH3d6fDoiHeNjCh/H2U7iw6Odqm8pQnGLHxW6ZV1w=="],
|
||||
|
||||
"@cspotcode/source-map-support": ["@cspotcode/source-map-support@0.8.1", "", { "dependencies": { "@jridgewell/trace-mapping": "0.3.9" } }, "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw=="],
|
||||
|
||||
@@ -1420,7 +1423,7 @@
|
||||
|
||||
"@jsonjoy.com/util": ["@jsonjoy.com/util@1.8.0", "", { "peerDependencies": { "tslib": "2" } }, "sha512-HeR0JQNEdBozt+FrfyM5T0X3R+fIN0D+BRDkxPP5o41fTWzHfeZEqtK16aTW8haU+h+SG7XYq9PP5kobvOmkSA=="],
|
||||
|
||||
"@kitware/vtk.js": ["@kitware/vtk.js@32.12.0", "", { "dependencies": { "@babel/runtime": "7.22.11", "@types/webxr": "^0.5.5", "commander": "9.2.0", "d3-scale": "4.0.2", "fast-deep-equal": "^3.1.3", "fflate": "0.7.3", "gl-matrix": "3.4.3", "globalthis": "1.0.3", "seedrandom": "3.0.5", "shader-loader": "1.3.1", "shelljs": "0.8.5", "spark-md5": "3.0.2", "stream-browserify": "3.0.0", "utif": "3.1.0", "webworker-promise": "0.5.0", "worker-loader": "3.0.8", "xmlbuilder2": "3.0.2" }, "peerDependencies": { "@babel/preset-env": "^7.17.10", "autoprefixer": "^10.4.7", "wslink": ">=1.1.0 || ^2.0.0" }, "bin": { "xml2json": "Utilities/XMLConverter/xml2json-cli.js", "vtkDataConverter": "Utilities/DataGenerator/convert-cli.js" } }, "sha512-QKO2BCAtNsl0p62RoA7vJbQ2NAJPyf0upPddOoNC+Qpfa1gPTGDatdyPm2/hLF9mYov6Lom7dEpfSVv8/bekzA=="],
|
||||
"@kitware/vtk.js": ["@kitware/vtk.js@34.15.1", "", { "dependencies": { "@babel/runtime": "^7.28.2", "@types/webxr": "^0.5.5", "commander": "9.2.0", "d3-scale": "4.0.2", "fast-deep-equal": "^3.1.3", "fflate": "0.7.3", "gl-matrix": "3.4.3", "globalthis": "1.0.3", "seedrandom": "3.0.5", "shader-loader": "1.3.1", "shelljs": "0.8.5", "spark-md5": "3.0.2", "stream-browserify": "3.0.0", "utif": "3.1.0", "webworker-promise": "0.5.0", "worker-loader": "3.0.8", "xmlbuilder2": "3.0.2" }, "peerDependencies": { "@babel/preset-env": "^7.17.10", "autoprefixer": "^10.4.7", "wslink": ">=1.1.0 || ^2.0.0" }, "bin": { "vtkDataConverter": "Utilities/DataGenerator/convert-cli.js", "xml2json": "Utilities/XMLConverter/xml2json-cli.js" } }, "sha512-9BH/a++drhclknBKNVzVpcOzDK95nc/OflKpQE8nY+IlaXTB1+Z6WXS6IwkEEUkRRUqSVskcW4ksgWdxihB49w=="],
|
||||
|
||||
"@leichtgewicht/ip-codec": ["@leichtgewicht/ip-codec@2.0.5", "", {}, "sha512-Vo+PSpZG2/fmgmiNzYK9qWRh8h/CHrwD0mo1h1DzL4yzHNSfWYujGTYsWGreD000gcgmZ7K4Ys6Tx9TxtsKdDw=="],
|
||||
|
||||
@@ -1610,7 +1613,7 @@
|
||||
|
||||
"@pkgr/core": ["@pkgr/core@0.2.9", "", {}, "sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA=="],
|
||||
|
||||
"@playwright/test": ["@playwright/test@1.54.1", "", { "dependencies": { "playwright": "1.54.1" }, "bin": { "playwright": "cli.js" } }, "sha512-FS8hQ12acieG2dYSksmLOF7BNxnVf2afRJdCuM1eMSxj6QTSE6G4InGF7oApGgDb65MX7AwMVlIkpru0yZA4Xw=="],
|
||||
"@playwright/test": ["@playwright/test@1.56.1", "", { "dependencies": { "playwright": "1.56.1" }, "bin": { "playwright": "cli.js" } }, "sha512-vSMYtL/zOcFpvJCW71Q/OEGQb7KYBPAdKh35WNSkaZA75JlAO8ED8UN6GUNTm3drWomcbcqRPFqQbLae8yBTdg=="],
|
||||
|
||||
"@pmmmwh/react-refresh-webpack-plugin": ["@pmmmwh/react-refresh-webpack-plugin@0.5.17", "", { "dependencies": { "ansi-html": "^0.0.9", "core-js-pure": "^3.23.3", "error-stack-parser": "^2.0.6", "html-entities": "^2.1.0", "loader-utils": "^2.0.4", "schema-utils": "^4.2.0", "source-map": "^0.7.3" }, "peerDependencies": { "@types/webpack": "4.x || 5.x", "react-refresh": ">=0.10.0 <1.0.0", "sockjs-client": "^1.4.0", "type-fest": ">=0.17.0 <5.0.0", "webpack": ">=4.43.0 <6.0.0", "webpack-dev-server": "3.x || 4.x || 5.x", "webpack-hot-middleware": "2.x", "webpack-plugin-serve": "0.x || 1.x" }, "optionalPeers": ["@types/webpack", "sockjs-client", "type-fest", "webpack-dev-server", "webpack-hot-middleware", "webpack-plugin-serve"] }, "sha512-tXDyE1/jzFsHXjhRZQ3hMl0IVhYe5qula43LDWIhVfjp9G/nT5OQY5AORVOrkEGAUltBJOfOWeETbmhm6kHhuQ=="],
|
||||
|
||||
@@ -2678,7 +2681,7 @@
|
||||
|
||||
"dayjs": ["dayjs@1.11.13", "", {}, "sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg=="],
|
||||
|
||||
"dcmjs": ["dcmjs@0.43.1", "", { "dependencies": { "@babel/runtime-corejs3": "^7.22.5", "adm-zip": "^0.5.10", "gl-matrix": "^3.1.0", "lodash.clonedeep": "^4.5.0", "loglevel": "^1.8.1", "ndarray": "^1.0.19", "pako": "^2.0.4" } }, "sha512-7IAB2cs2F8QYINbfhA7PV+IDraqBuHn8N31xJc6HsyNxZXyGUPQRujcq732ACqjLhl7oJi1z8PJJgLdblQtD3Q=="],
|
||||
"dcmjs": ["dcmjs@0.45.0", "", { "dependencies": { "@babel/runtime-corejs3": "^7.22.5", "adm-zip": "^0.5.10", "gl-matrix": "^3.1.0", "lodash.clonedeep": "^4.5.0", "loglevel": "^1.8.1", "ndarray": "^1.0.19", "pako": "^2.0.4" } }, "sha512-MFtACBLQB6vj/3lzHEf6iGBLFC8f/QysoDO+DVUJeLrKYQi4mXYzzBbHmZCpMXqIpj8qXXLvnP/u2ugd9DVu7Q=="],
|
||||
|
||||
"debounce": ["debounce@1.2.1", "", {}, "sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug=="],
|
||||
|
||||
@@ -3952,7 +3955,7 @@
|
||||
|
||||
"node-fetch": ["node-fetch@3.3.2", "", { "dependencies": { "data-uri-to-buffer": "^4.0.0", "fetch-blob": "^3.1.4", "formdata-polyfill": "^4.0.10" } }, "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA=="],
|
||||
|
||||
"node-forge": ["node-forge@1.3.1", "", {}, "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA=="],
|
||||
"node-forge": ["node-forge@1.3.2", "", {}, "sha512-6xKiQ+cph9KImrRh0VsjH2d8/GXA4FIMlgU4B757iI1ApvcyA9VlouP0yZJha01V+huImO+kKMU7ih+2+E14fw=="],
|
||||
|
||||
"node-gyp": ["node-gyp@9.4.1", "", { "dependencies": { "env-paths": "^2.2.0", "exponential-backoff": "^3.1.1", "glob": "^7.1.4", "graceful-fs": "^4.2.6", "make-fetch-happen": "^10.0.3", "nopt": "^6.0.0", "npmlog": "^6.0.0", "rimraf": "^3.0.2", "semver": "^7.3.5", "tar": "^6.1.2", "which": "^2.0.2" }, "bin": { "node-gyp": "bin/node-gyp.js" } }, "sha512-OQkWKbjQKbGkMf/xqI1jjy3oCTgMKJac58G2+bjZb3fza6gW2YrCSdMQYaoTb70crvE//Gngr4f0AgVHmqHvBQ=="],
|
||||
|
||||
@@ -4170,9 +4173,9 @@
|
||||
|
||||
"platform": ["platform@1.3.6", "", {}, "sha512-fnWVljUchTro6RiCFvCXBbNhJc2NijN7oIQxbwsyL0buWJPG85v81ehlHI9fXrJsMNgTofEoWIQeClKpgxFLrg=="],
|
||||
|
||||
"playwright": ["playwright@1.54.1", "", { "dependencies": { "playwright-core": "1.54.1" }, "optionalDependencies": { "fsevents": "2.3.2" }, "bin": { "playwright": "cli.js" } }, "sha512-peWpSwIBmSLi6aW2auvrUtf2DqY16YYcCMO8rTVx486jKmDTJg7UAhyrraP98GB8BoPURZP8+nxO7TSd4cPr5g=="],
|
||||
"playwright": ["playwright@1.56.1", "", { "dependencies": { "playwright-core": "1.56.1" }, "optionalDependencies": { "fsevents": "2.3.2" }, "bin": { "playwright": "cli.js" } }, "sha512-aFi5B0WovBHTEvpM3DzXTUaeN6eN0qWnTkKx4NQaH4Wvcmc153PdaY2UBdSYKaGYw+UyWXSVyxDUg5DoPEttjw=="],
|
||||
|
||||
"playwright-core": ["playwright-core@1.54.1", "", { "bin": { "playwright-core": "cli.js" } }, "sha512-Nbjs2zjj0htNhzgiy5wu+3w09YetDx5pkrpI/kZotDlDUaYk0HVA5xrBVPdow4SAUIlhgKcJeJg4GRKW6xHusA=="],
|
||||
"playwright-core": ["playwright-core@1.56.1", "", { "bin": { "playwright-core": "cli.js" } }, "sha512-hutraynyn31F+Bifme+Ps9Vq59hKuUCz7H1kDOcBs+2oGguKkWTU50bBWrtz34OUWmIwpBTWDxaRPXrIXkgvmQ=="],
|
||||
|
||||
"playwright-test-coverage": ["playwright-test-coverage@1.2.12", "", { "peerDependencies": { "@playwright/test": "^1.14.1" } }, "sha512-WdR3shV+7IWtlB1AZcXSysUyZmyAqXM9+DT3iBSS1p4SkJoWZslkTqk04l5ZExSkuaM+5fKl0M9TyUuJWuGgLA=="],
|
||||
|
||||
@@ -5234,14 +5237,6 @@
|
||||
|
||||
"@cornerstonejs/ai/buffer": ["buffer@6.0.3", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.2.1" } }, "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA=="],
|
||||
|
||||
"@cornerstonejs/core/@kitware/vtk.js": ["@kitware/vtk.js@32.12.1", "", { "dependencies": { "@babel/runtime": "7.22.11", "@types/webxr": "^0.5.5", "commander": "9.2.0", "d3-scale": "4.0.2", "fast-deep-equal": "^3.1.3", "fflate": "0.7.3", "gl-matrix": "3.4.3", "globalthis": "1.0.3", "seedrandom": "3.0.5", "shader-loader": "1.3.1", "shelljs": "0.8.5", "spark-md5": "3.0.2", "stream-browserify": "3.0.0", "utif": "3.1.0", "webworker-promise": "0.5.0", "worker-loader": "3.0.8", "xmlbuilder2": "3.0.2" }, "peerDependencies": { "@babel/preset-env": "^7.17.10", "autoprefixer": "^10.4.7", "wslink": ">=1.1.0 || ^2.0.0" }, "bin": { "vtkDataConverter": "Utilities/DataGenerator/convert-cli.js", "xml2json": "Utilities/XMLConverter/xml2json-cli.js" } }, "sha512-IF8WK7WDu84035ws3HZXz8wys8O81mmeFayYFbdBEfKjgZ4k0EGCK25YCGc6Q47Zxq1iutjs3Zs2xAgGjTgGQg=="],
|
||||
|
||||
"@cornerstonejs/labelmap-interpolation/@kitware/vtk.js": ["@kitware/vtk.js@32.12.1", "", { "dependencies": { "@babel/runtime": "7.22.11", "@types/webxr": "^0.5.5", "commander": "9.2.0", "d3-scale": "4.0.2", "fast-deep-equal": "^3.1.3", "fflate": "0.7.3", "gl-matrix": "3.4.3", "globalthis": "1.0.3", "seedrandom": "3.0.5", "shader-loader": "1.3.1", "shelljs": "0.8.5", "spark-md5": "3.0.2", "stream-browserify": "3.0.0", "utif": "3.1.0", "webworker-promise": "0.5.0", "worker-loader": "3.0.8", "xmlbuilder2": "3.0.2" }, "peerDependencies": { "@babel/preset-env": "^7.17.10", "autoprefixer": "^10.4.7", "wslink": ">=1.1.0 || ^2.0.0" }, "bin": { "vtkDataConverter": "Utilities/DataGenerator/convert-cli.js", "xml2json": "Utilities/XMLConverter/xml2json-cli.js" } }, "sha512-IF8WK7WDu84035ws3HZXz8wys8O81mmeFayYFbdBEfKjgZ4k0EGCK25YCGc6Q47Zxq1iutjs3Zs2xAgGjTgGQg=="],
|
||||
|
||||
"@cornerstonejs/polymorphic-segmentation/@kitware/vtk.js": ["@kitware/vtk.js@32.12.1", "", { "dependencies": { "@babel/runtime": "7.22.11", "@types/webxr": "^0.5.5", "commander": "9.2.0", "d3-scale": "4.0.2", "fast-deep-equal": "^3.1.3", "fflate": "0.7.3", "gl-matrix": "3.4.3", "globalthis": "1.0.3", "seedrandom": "3.0.5", "shader-loader": "1.3.1", "shelljs": "0.8.5", "spark-md5": "3.0.2", "stream-browserify": "3.0.0", "utif": "3.1.0", "webworker-promise": "0.5.0", "worker-loader": "3.0.8", "xmlbuilder2": "3.0.2" }, "peerDependencies": { "@babel/preset-env": "^7.17.10", "autoprefixer": "^10.4.7", "wslink": ">=1.1.0 || ^2.0.0" }, "bin": { "vtkDataConverter": "Utilities/DataGenerator/convert-cli.js", "xml2json": "Utilities/XMLConverter/xml2json-cli.js" } }, "sha512-IF8WK7WDu84035ws3HZXz8wys8O81mmeFayYFbdBEfKjgZ4k0EGCK25YCGc6Q47Zxq1iutjs3Zs2xAgGjTgGQg=="],
|
||||
|
||||
"@cornerstonejs/tools/@kitware/vtk.js": ["@kitware/vtk.js@32.12.1", "", { "dependencies": { "@babel/runtime": "7.22.11", "@types/webxr": "^0.5.5", "commander": "9.2.0", "d3-scale": "4.0.2", "fast-deep-equal": "^3.1.3", "fflate": "0.7.3", "gl-matrix": "3.4.3", "globalthis": "1.0.3", "seedrandom": "3.0.5", "shader-loader": "1.3.1", "shelljs": "0.8.5", "spark-md5": "3.0.2", "stream-browserify": "3.0.0", "utif": "3.1.0", "webworker-promise": "0.5.0", "worker-loader": "3.0.8", "xmlbuilder2": "3.0.2" }, "peerDependencies": { "@babel/preset-env": "^7.17.10", "autoprefixer": "^10.4.7", "wslink": ">=1.1.0 || ^2.0.0" }, "bin": { "vtkDataConverter": "Utilities/DataGenerator/convert-cli.js", "xml2json": "Utilities/XMLConverter/xml2json-cli.js" } }, "sha512-IF8WK7WDu84035ws3HZXz8wys8O81mmeFayYFbdBEfKjgZ4k0EGCK25YCGc6Q47Zxq1iutjs3Zs2xAgGjTgGQg=="],
|
||||
|
||||
"@cspotcode/source-map-support/@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.9", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.0.3", "@jridgewell/sourcemap-codec": "^1.4.10" } }, "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ=="],
|
||||
|
||||
"@cypress/request/uuid": ["uuid@8.3.2", "", { "bin": { "uuid": "dist/bin/uuid" } }, "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg=="],
|
||||
@@ -5304,8 +5299,6 @@
|
||||
|
||||
"@jest/types/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="],
|
||||
|
||||
"@kitware/vtk.js/@babel/runtime": ["@babel/runtime@7.22.11", "", { "dependencies": { "regenerator-runtime": "^0.14.0" } }, "sha512-ee7jVNlWN09+KftVOu9n7S8gQzD/Z6hN/I8VBRXW4P1+Xe7kJGXMwu8vds4aGIMHZnNbdpSWCfZZtinytpcAvA=="],
|
||||
|
||||
"@lerna/child-process/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="],
|
||||
|
||||
"@lerna/child-process/execa": ["execa@5.0.0", "", { "dependencies": { "cross-spawn": "^7.0.3", "get-stream": "^6.0.0", "human-signals": "^2.1.0", "is-stream": "^2.0.0", "merge-stream": "^2.0.0", "npm-run-path": "^4.0.1", "onetime": "^5.1.2", "signal-exit": "^3.0.3", "strip-final-newline": "^2.0.0" } }, "sha512-ov6w/2LCiuyO4RLYGdpFGjkcs0wMTgGE8PrkTHikeUy5iJekXyPIKUjifk5CsE0pt7sMCrMZ3YNqoCj6idQOnQ=="],
|
||||
@@ -6328,14 +6321,6 @@
|
||||
|
||||
"@apideck/better-ajv-errors/ajv/json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="],
|
||||
|
||||
"@cornerstonejs/core/@kitware/vtk.js/@babel/runtime": ["@babel/runtime@7.22.11", "", { "dependencies": { "regenerator-runtime": "^0.14.0" } }, "sha512-ee7jVNlWN09+KftVOu9n7S8gQzD/Z6hN/I8VBRXW4P1+Xe7kJGXMwu8vds4aGIMHZnNbdpSWCfZZtinytpcAvA=="],
|
||||
|
||||
"@cornerstonejs/labelmap-interpolation/@kitware/vtk.js/@babel/runtime": ["@babel/runtime@7.22.11", "", { "dependencies": { "regenerator-runtime": "^0.14.0" } }, "sha512-ee7jVNlWN09+KftVOu9n7S8gQzD/Z6hN/I8VBRXW4P1+Xe7kJGXMwu8vds4aGIMHZnNbdpSWCfZZtinytpcAvA=="],
|
||||
|
||||
"@cornerstonejs/polymorphic-segmentation/@kitware/vtk.js/@babel/runtime": ["@babel/runtime@7.22.11", "", { "dependencies": { "regenerator-runtime": "^0.14.0" } }, "sha512-ee7jVNlWN09+KftVOu9n7S8gQzD/Z6hN/I8VBRXW4P1+Xe7kJGXMwu8vds4aGIMHZnNbdpSWCfZZtinytpcAvA=="],
|
||||
|
||||
"@cornerstonejs/tools/@kitware/vtk.js/@babel/runtime": ["@babel/runtime@7.22.11", "", { "dependencies": { "regenerator-runtime": "^0.14.0" } }, "sha512-ee7jVNlWN09+KftVOu9n7S8gQzD/Z6hN/I8VBRXW4P1+Xe7kJGXMwu8vds4aGIMHZnNbdpSWCfZZtinytpcAvA=="],
|
||||
|
||||
"@eslint/eslintrc/js-yaml/argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="],
|
||||
|
||||
"@isaacs/cliui/strip-ansi/ansi-regex": ["ansi-regex@6.1.0", "", {}, "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA=="],
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
[install]
|
||||
frozenLockfile = true
|
||||
@@ -0,0 +1,2 @@
|
||||
[install]
|
||||
frozenLockfile = false
|
||||
+1
-1
@@ -1 +1 @@
|
||||
3137aed0170196a61e16c6dac0c15bcc41691919
|
||||
1aa443112186851855a638ab974a3060c3e28ea0
|
||||
@@ -1,118 +0,0 @@
|
||||
diff --git a/extensions/cornerstone/src/components/ViewportOrientationMenu/ViewportOrientationMenu.tsx b/extensions/cornerstone/src/components/ViewportOrientationMenu/ViewportOrientationMenu.tsx
|
||||
index 6c1e4f8f6..56dac15b5 100644
|
||||
--- a/extensions/cornerstone/src/components/ViewportOrientationMenu/ViewportOrientationMenu.tsx
|
||||
+++ b/extensions/cornerstone/src/components/ViewportOrientationMenu/ViewportOrientationMenu.tsx
|
||||
@@ -156,65 +156,65 @@ function ViewportOrientationMenu({
|
||||
>
|
||||
<Button
|
||||
variant="ghost"
|
||||
- className="flex h-[28px] flex-shrink-0 items-center justify-start self-stretch"
|
||||
+ className="flex h-7 w-full flex-shrink-0 items-center justify-start self-stretch px-1 py-0"
|
||||
onClick={() => handleOrientationChange('axial')}
|
||||
>
|
||||
- <span className="mr-0 flex w-4 items-center justify-center">
|
||||
+ <div className="mr-1 flex w-6 items-center justify-start">
|
||||
{currentOrientation === 'axial' ? (
|
||||
- <Icons.Checked className="text-primary h-5 w-5" />
|
||||
+ <Icons.Checked className="text-primary h-6 w-6" />
|
||||
) : null}
|
||||
- </span>
|
||||
- <span className="flex-1 text-left">Axial</span>
|
||||
+ </div>
|
||||
+ <div className="flex-1 text-left">Axial</div>
|
||||
</Button>
|
||||
<Button
|
||||
variant="ghost"
|
||||
- className="flex h-[28px] flex-shrink-0 items-center justify-start self-stretch"
|
||||
+ className="flex h-7 w-full flex-shrink-0 items-center justify-start self-stretch px-1 py-0"
|
||||
onClick={() => handleOrientationChange('sagittal')}
|
||||
>
|
||||
- <span className="mr-0 flex w-4 items-center justify-center">
|
||||
+ <div className="mr-1 flex w-6 items-center justify-start">
|
||||
{currentOrientation === 'sagittal' ? (
|
||||
- <Icons.Checked className="text-primary h-5 w-5" />
|
||||
+ <Icons.Checked className="text-primary h-6 w-6" />
|
||||
) : null}
|
||||
- </span>
|
||||
- <span className="flex-1 text-left">Sagittal</span>
|
||||
+ </div>
|
||||
+ <div className="flex-1 text-left">Sagittal</div>
|
||||
</Button>
|
||||
<Button
|
||||
variant="ghost"
|
||||
- className="flex h-[28px] flex-shrink-0 items-center justify-start self-stretch"
|
||||
+ className="flex h-7 w-full flex-shrink-0 items-center justify-start self-stretch px-1 py-0"
|
||||
onClick={() => handleOrientationChange('coronal')}
|
||||
>
|
||||
- <span className="mr-0 flex w-4 items-center justify-center">
|
||||
+ <div className="mr-1 flex w-6 items-center justify-start">
|
||||
{currentOrientation === 'coronal' ? (
|
||||
- <Icons.Checked className="text-primary h-5 w-5" />
|
||||
+ <Icons.Checked className="text-primary h-6 w-6" />
|
||||
) : null}
|
||||
- </span>
|
||||
- <span className="flex-1 text-left">Coronal</span>
|
||||
+ </div>
|
||||
+ <div className="flex-1 text-left">Coronal</div>
|
||||
</Button>
|
||||
<Button
|
||||
variant="ghost"
|
||||
- className="flex h-[28px] flex-shrink-0 items-center justify-start self-stretch"
|
||||
+ className="flex h-7 w-full flex-shrink-0 items-center justify-start self-stretch px-1 py-0"
|
||||
onClick={() => handleOrientationChange('acquisition')}
|
||||
>
|
||||
- <span className="mr-0 flex w-4 items-center justify-center">
|
||||
+ <div className="mr-1 flex w-6 items-center justify-start">
|
||||
{currentOrientation === 'acquisition' ? (
|
||||
- <Icons.Checked className="text-primary h-5 w-5" />
|
||||
+ <Icons.Checked className="text-primary h-6 w-6" />
|
||||
) : null}
|
||||
- </span>
|
||||
- <span className="flex-1 text-left">Acquisition</span>
|
||||
+ </div>
|
||||
+ <div className="flex-1 text-left">Acquisition</div>
|
||||
</Button>
|
||||
{/* Divider */}
|
||||
- <div className="my-2 border-t border-white/20" />
|
||||
+ <div className="mx-1 my-2 border-t border-white/20" />
|
||||
<Button
|
||||
variant="ghost"
|
||||
- className="flex h-[28px] flex-shrink-0 items-center justify-start self-stretch"
|
||||
+ className="flex h-7 w-full flex-shrink-0 items-center justify-start self-stretch px-1 py-0"
|
||||
onClick={() => handleOrientationChange('reformat')}
|
||||
>
|
||||
- <span className="mr-0 flex w-4 items-center justify-center">
|
||||
+ <div className="mr-1 flex w-6 items-center justify-start">
|
||||
{currentOrientation === 'reformat' ? (
|
||||
- <Icons.Checked className="text-primary h-5 w-5" />
|
||||
+ <Icons.Checked className="text-primary h-6 w-6" />
|
||||
) : null}
|
||||
- </span>
|
||||
- <span className="flex-1 text-left">Reformat</span>
|
||||
+ </div>
|
||||
+ <div className="flex-1 text-left">Reformat</div>
|
||||
</Button>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
diff --git a/platform/ui-next/src/components/Icons/Sources/Checked.tsx b/platform/ui-next/src/components/Icons/Sources/Checked.tsx
|
||||
index 83c8fc57e..c88b75e0d 100644
|
||||
--- a/platform/ui-next/src/components/Icons/Sources/Checked.tsx
|
||||
+++ b/platform/ui-next/src/components/Icons/Sources/Checked.tsx
|
||||
@@ -4,14 +4,14 @@ import type { IconProps } from '../types';
|
||||
export const Checked = (props: IconProps) => (
|
||||
<svg
|
||||
{...props}
|
||||
- width="24"
|
||||
+ width="20"
|
||||
height="24"
|
||||
- viewBox="0 0 24 24"
|
||||
+ viewBox="0 0 20 24"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
- d="M16 9L11.3333 15L8 12.4737"
|
||||
+ d="M14 9L9.3333 15L6 12.4737"
|
||||
stroke="currentColor"
|
||||
strokeWidth="1.5"
|
||||
strokeLinecap="round"
|
||||
@@ -3,6 +3,307 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [3.12.0-beta.97](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.96...v3.12.0-beta.97) (2025-12-03)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-pmap
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.96](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.95...v3.12.0-beta.96) (2025-12-02)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-pmap
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.95](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.94...v3.12.0-beta.95) (2025-12-02)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* sculptor tool fixes ([#5595](https://github.com/OHIF/Viewers/issues/5595)) ([4471416](https://github.com/OHIF/Viewers/commit/4471416c30bf80d9f6766660d103f28c83ba7bb3))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.94](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.93...v3.12.0-beta.94) (2025-12-02)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-pmap
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.93](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.92...v3.12.0-beta.93) (2025-11-25)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-pmap
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.92](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.91...v3.12.0-beta.92) (2025-11-24)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-pmap
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.91](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.90...v3.12.0-beta.91) (2025-11-21)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-pmap
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.90](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.89...v3.12.0-beta.90) (2025-11-21)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-pmap
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.89](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.88...v3.12.0-beta.89) (2025-11-20)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-pmap
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.88](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.87...v3.12.0-beta.88) (2025-11-20)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-pmap
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.87](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.86...v3.12.0-beta.87) (2025-11-19)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **SegmentationStyle:** Fix inactive contour visibility and styling. ([#5563](https://github.com/OHIF/Viewers/issues/5563)) ([5c17c26](https://github.com/OHIF/Viewers/commit/5c17c262eaa5c1229033cfff4b3e7709e874611f))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.86](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.85...v3.12.0-beta.86) (2025-11-13)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-pmap
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.85](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.84...v3.12.0-beta.85) (2025-11-11)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **interpolation:** Auto accept interpolation when the interpolation process is completed. ([#5555](https://github.com/OHIF/Viewers/issues/5555)) ([834ae3c](https://github.com/OHIF/Viewers/commit/834ae3c762ca309187afebdf77bad1ad9a57f03f))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.84](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.83...v3.12.0-beta.84) (2025-11-10)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-pmap
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.83](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.82...v3.12.0-beta.83) (2025-11-06)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-pmap
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.82](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.81...v3.12.0-beta.82) (2025-11-05)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-pmap
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.81](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.80...v3.12.0-beta.81) (2025-11-05)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **SegmentationTools:** [Bug] Changes of brush/eraser radius with hotkey do not reflect on segmentation tool ([#5535](https://github.com/OHIF/Viewers/issues/5535)) ([29bd87c](https://github.com/OHIF/Viewers/commit/29bd87c8c0592a9a1350e687b862f5452627aece))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.80](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.79...v3.12.0-beta.80) (2025-11-05)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-pmap
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.79](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.78...v3.12.0-beta.79) (2025-10-30)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-pmap
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.78](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.77...v3.12.0-beta.78) (2025-10-29)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-pmap
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.77](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.76...v3.12.0-beta.77) (2025-10-28)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **3DSegmentation:** [Bug] The viewports become blank when loading the seg file in advanced layout after closing the seg file from any other advanced layout ([#5505](https://github.com/OHIF/Viewers/issues/5505)) ([76f7d4e](https://github.com/OHIF/Viewers/commit/76f7d4e23aa359a3397b10adf0bf09df3763f190))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.76](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.75...v3.12.0-beta.76) (2025-10-27)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-pmap
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.75](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.74...v3.12.0-beta.75) (2025-10-24)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-pmap
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.74](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.73...v3.12.0-beta.74) (2025-10-23)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-pmap
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.73](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.72...v3.12.0-beta.73) (2025-10-22)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-pmap
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.72](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.71...v3.12.0-beta.72) (2025-10-22)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-pmap
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.71](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.70...v3.12.0-beta.71) (2025-10-21)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-pmap
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.70](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.69...v3.12.0-beta.70) (2025-10-20)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-pmap
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.69](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.68...v3.12.0-beta.69) (2025-10-20)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Pass the correct sop uid + frame for rehydration to prevent associating rehydrated measurements with the wrong data ([#5506](https://github.com/OHIF/Viewers/issues/5506)) ([5037802](https://github.com/OHIF/Viewers/commit/503780247ce7228c602a539fd88ea374e7d0d42f)), closes [#2404](https://github.com/OHIF/Viewers/issues/2404)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.68](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.67...v3.12.0-beta.68) (2025-10-18)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **segmentation:** Lock all rehydrated segmentation segments when panelSegmentation.disableEditing is true. ([#5503](https://github.com/OHIF/Viewers/issues/5503)) ([170e860](https://github.com/OHIF/Viewers/commit/170e860aa1b786ab6056cf31076804302a2bc462))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.67](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.66...v3.12.0-beta.67) (2025-10-17)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-pmap
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.66](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.65...v3.12.0-beta.66) (2025-10-17)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-pmap
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.65](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.64...v3.12.0-beta.65) (2025-10-17)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-pmap
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.64](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.63...v3.12.0-beta.64) (2025-10-15)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-pmap
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.63](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.62...v3.12.0-beta.63) (2025-10-15)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-pmap
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.62](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.61...v3.12.0-beta.62) (2025-10-15)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-pmap
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@ohif/extension-cornerstone-dicom-pmap",
|
||||
"version": "3.12.0-beta.62",
|
||||
"version": "3.12.0-beta.97",
|
||||
"description": "DICOM Parametric Map read workflow",
|
||||
"author": "OHIF",
|
||||
"license": "MIT",
|
||||
@@ -33,10 +33,10 @@
|
||||
"start": "yarn run dev"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@ohif/core": "3.12.0-beta.62",
|
||||
"@ohif/extension-cornerstone": "3.12.0-beta.62",
|
||||
"@ohif/extension-default": "3.12.0-beta.62",
|
||||
"@ohif/i18n": "3.12.0-beta.62",
|
||||
"@ohif/core": "3.12.0-beta.97",
|
||||
"@ohif/extension-cornerstone": "3.12.0-beta.97",
|
||||
"@ohif/extension-default": "3.12.0-beta.97",
|
||||
"@ohif/i18n": "3.12.0-beta.97",
|
||||
"prop-types": "15.8.1",
|
||||
"react": "18.3.1",
|
||||
"react-dom": "18.3.1",
|
||||
@@ -46,9 +46,9 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": "7.28.2",
|
||||
"@cornerstonejs/adapters": "4.5.5",
|
||||
"@cornerstonejs/core": "4.5.5",
|
||||
"@kitware/vtk.js": "32.12.0",
|
||||
"@cornerstonejs/adapters": "4.12.6",
|
||||
"@cornerstonejs/core": "4.12.6",
|
||||
"@kitware/vtk.js": "34.15.1",
|
||||
"react-color": "2.19.3"
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,286 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [3.12.0-beta.97](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.96...v3.12.0-beta.97) (2025-12-03)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.96](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.95...v3.12.0-beta.96) (2025-12-02)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.95](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.94...v3.12.0-beta.95) (2025-12-02)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.94](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.93...v3.12.0-beta.94) (2025-12-02)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.93](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.92...v3.12.0-beta.93) (2025-11-25)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.92](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.91...v3.12.0-beta.92) (2025-11-24)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.91](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.90...v3.12.0-beta.91) (2025-11-21)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.90](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.89...v3.12.0-beta.90) (2025-11-21)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.89](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.88...v3.12.0-beta.89) (2025-11-20)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.88](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.87...v3.12.0-beta.88) (2025-11-20)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.87](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.86...v3.12.0-beta.87) (2025-11-19)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.86](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.85...v3.12.0-beta.86) (2025-11-13)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.85](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.84...v3.12.0-beta.85) (2025-11-11)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.84](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.83...v3.12.0-beta.84) (2025-11-10)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.83](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.82...v3.12.0-beta.83) (2025-11-06)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.82](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.81...v3.12.0-beta.82) (2025-11-05)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.81](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.80...v3.12.0-beta.81) (2025-11-05)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.80](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.79...v3.12.0-beta.80) (2025-11-05)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.79](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.78...v3.12.0-beta.79) (2025-10-30)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.78](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.77...v3.12.0-beta.78) (2025-10-29)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.77](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.76...v3.12.0-beta.77) (2025-10-28)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.76](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.75...v3.12.0-beta.76) (2025-10-27)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.75](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.74...v3.12.0-beta.75) (2025-10-24)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.74](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.73...v3.12.0-beta.74) (2025-10-23)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.73](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.72...v3.12.0-beta.73) (2025-10-22)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.72](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.71...v3.12.0-beta.72) (2025-10-22)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.71](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.70...v3.12.0-beta.71) (2025-10-21)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.70](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.69...v3.12.0-beta.70) (2025-10-20)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.69](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.68...v3.12.0-beta.69) (2025-10-20)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.68](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.67...v3.12.0-beta.68) (2025-10-18)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.67](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.66...v3.12.0-beta.67) (2025-10-17)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.66](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.65...v3.12.0-beta.66) (2025-10-17)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.65](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.64...v3.12.0-beta.65) (2025-10-17)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.64](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.63...v3.12.0-beta.64) (2025-10-15)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.63](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.62...v3.12.0-beta.63) (2025-10-15)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.62](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.61...v3.12.0-beta.62) (2025-10-15)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-rt
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@ohif/extension-cornerstone-dicom-rt",
|
||||
"version": "3.12.0-beta.62",
|
||||
"version": "3.12.0-beta.97",
|
||||
"description": "DICOM RT read workflow",
|
||||
"author": "OHIF",
|
||||
"license": "MIT",
|
||||
@@ -33,10 +33,10 @@
|
||||
"start": "yarn run dev"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@ohif/core": "3.12.0-beta.62",
|
||||
"@ohif/extension-cornerstone": "3.12.0-beta.62",
|
||||
"@ohif/extension-default": "3.12.0-beta.62",
|
||||
"@ohif/i18n": "3.12.0-beta.62",
|
||||
"@ohif/core": "3.12.0-beta.97",
|
||||
"@ohif/extension-cornerstone": "3.12.0-beta.97",
|
||||
"@ohif/extension-default": "3.12.0-beta.97",
|
||||
"@ohif/i18n": "3.12.0-beta.97",
|
||||
"prop-types": "15.8.1",
|
||||
"react": "18.3.1",
|
||||
"react-dom": "18.3.1",
|
||||
|
||||
@@ -17,19 +17,25 @@ function _getDisplaySetsFromSeries(
|
||||
servicesManager: AppTypes.ServicesManager,
|
||||
extensionManager
|
||||
) {
|
||||
const instance = instances[0];
|
||||
utils.sortStudyInstances(instances);
|
||||
// Choose the LAST instance in the list as the most recently created one.
|
||||
const instance = instances[instances.length - 1];
|
||||
|
||||
const {
|
||||
StudyInstanceUID,
|
||||
SeriesInstanceUID,
|
||||
SOPInstanceUID,
|
||||
SeriesDescription,
|
||||
SeriesDescription = '',
|
||||
SeriesNumber,
|
||||
SeriesDate,
|
||||
SeriesTime,
|
||||
StructureSetDate,
|
||||
StructureSetTime,
|
||||
SOPClassUID,
|
||||
wadoRoot,
|
||||
wadoUri,
|
||||
wadoUriRoot,
|
||||
imageId: predecessorImageId,
|
||||
} = instance;
|
||||
|
||||
const displaySet = {
|
||||
@@ -39,7 +45,13 @@ function _getDisplaySetsFromSeries(
|
||||
displaySetInstanceUID: utils.guid(),
|
||||
SeriesDescription,
|
||||
SeriesNumber,
|
||||
SeriesDate,
|
||||
/**
|
||||
* The "SeriesDate" for a display set is really the display set date, which
|
||||
* should be the date of the instance being used, which will be the structure
|
||||
* set date in this case.
|
||||
*/
|
||||
SeriesDate: StructureSetDate || SeriesDate,
|
||||
SeriesTime: StructureSetTime || SeriesTime,
|
||||
SOPInstanceUID,
|
||||
SeriesInstanceUID,
|
||||
StudyInstanceUID,
|
||||
@@ -54,6 +66,8 @@ function _getDisplaySetsFromSeries(
|
||||
structureSet: null,
|
||||
sopClassUids,
|
||||
instance,
|
||||
instances,
|
||||
predecessorImageId,
|
||||
wadoRoot,
|
||||
wadoUriRoot,
|
||||
wadoUri,
|
||||
@@ -62,7 +76,10 @@ function _getDisplaySetsFromSeries(
|
||||
};
|
||||
|
||||
let referencedSeriesSequence = instance.ReferencedSeriesSequence;
|
||||
if (instance.ReferencedFrameOfReferenceSequence && !instance.ReferencedSeriesSequence) {
|
||||
if (
|
||||
instance.ReferencedFrameOfReferenceSequence?.RTReferencedStudySequence &&
|
||||
!instance.ReferencedSeriesSequence
|
||||
) {
|
||||
instance.ReferencedSeriesSequence = _deriveReferencedSeriesSequenceFromFrameOfReferenceSequence(
|
||||
instance.ReferencedFrameOfReferenceSequence
|
||||
);
|
||||
@@ -70,7 +87,8 @@ function _getDisplaySetsFromSeries(
|
||||
}
|
||||
|
||||
if (!referencedSeriesSequence) {
|
||||
throw new Error('ReferencedSeriesSequence is missing for the RTSTRUCT');
|
||||
console.error('ReferencedSeriesSequence is missing for the RTSTRUCT');
|
||||
return;
|
||||
}
|
||||
|
||||
const referencedSeries = referencedSeriesSequence[0];
|
||||
@@ -79,9 +97,13 @@ function _getDisplaySetsFromSeries(
|
||||
displaySet.referencedSeriesInstanceUID = referencedSeries.SeriesInstanceUID;
|
||||
|
||||
const { displaySetService } = servicesManager.services;
|
||||
const referencedDisplaySets = displaySetService.getDisplaySetsForSeries(
|
||||
displaySet.referencedSeriesInstanceUID
|
||||
);
|
||||
const referencedDisplaySets =
|
||||
displaySetService.getDisplaySetsForReferences(referencedSeriesSequence);
|
||||
if (referencedDisplaySets?.length > 1) {
|
||||
console.warn(
|
||||
'Reference applies to more than 1 display set for Contours, applying only to first display set'
|
||||
);
|
||||
}
|
||||
|
||||
if (!referencedDisplaySets || referencedDisplaySets.length === 0) {
|
||||
// Instead of throwing error, subscribe to display sets added
|
||||
@@ -97,7 +119,7 @@ function _getDisplaySetsFromSeries(
|
||||
}
|
||||
);
|
||||
} else {
|
||||
const referencedDisplaySet = referencedDisplaySets[0];
|
||||
const [referencedDisplaySet] = referencedDisplaySets;
|
||||
displaySet.referencedDisplaySetInstanceUID = referencedDisplaySet.displaySetInstanceUID;
|
||||
displaySet.isReconstructable = referencedDisplaySet.isReconstructable;
|
||||
}
|
||||
|
||||
@@ -89,9 +89,12 @@ async function checkAndLoadContourData({
|
||||
ROIContour.ContourSequence.forEach((Contour, index) => {
|
||||
const promise = resolvedPromises[index];
|
||||
if (promise.status === 'fulfilled') {
|
||||
if (Array.isArray(promise.value) && promise.value.every(Number.isFinite)) {
|
||||
if (
|
||||
Array.isArray(promise.value) &&
|
||||
promise.value.every(it => Number.isFinite(Number(it)))
|
||||
) {
|
||||
// If promise.value is already an array of numbers, use it directly
|
||||
Contour.ContourData = promise.value;
|
||||
Contour.ContourData = promise.value.map(Number);
|
||||
} else {
|
||||
// If the resolved promise value is a byte array (Blob), it needs to be decoded
|
||||
const uint8Array = new Uint8Array(promise.value);
|
||||
|
||||
@@ -103,7 +103,7 @@ function OHIFCornerstoneRTViewport(props: withAppTypes) {
|
||||
const { unsubscribe } = segmentationService.subscribe(
|
||||
segmentationService.EVENTS.SEGMENTATION_LOADING_COMPLETE,
|
||||
evt => {
|
||||
if (evt.rtDisplaySet.displaySetInstanceUID === rtDisplaySet.displaySetInstanceUID) {
|
||||
if (evt.rtDisplaySet?.displaySetInstanceUID === rtDisplaySet.displaySetInstanceUID) {
|
||||
setRtIsLoading(false);
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,307 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [3.12.0-beta.97](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.96...v3.12.0-beta.97) (2025-12-03)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-seg
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.96](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.95...v3.12.0-beta.96) (2025-12-02)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-seg
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.95](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.94...v3.12.0-beta.95) (2025-12-02)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* sculptor tool fixes ([#5595](https://github.com/OHIF/Viewers/issues/5595)) ([4471416](https://github.com/OHIF/Viewers/commit/4471416c30bf80d9f6766660d103f28c83ba7bb3))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.94](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.93...v3.12.0-beta.94) (2025-12-02)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-seg
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.93](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.92...v3.12.0-beta.93) (2025-11-25)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-seg
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.92](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.91...v3.12.0-beta.92) (2025-11-24)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-seg
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.91](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.90...v3.12.0-beta.91) (2025-11-21)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-seg
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.90](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.89...v3.12.0-beta.90) (2025-11-21)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-seg
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.89](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.88...v3.12.0-beta.89) (2025-11-20)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-seg
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.88](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.87...v3.12.0-beta.88) (2025-11-20)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-seg
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.87](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.86...v3.12.0-beta.87) (2025-11-19)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **SegmentationStyle:** Fix inactive contour visibility and styling. ([#5563](https://github.com/OHIF/Viewers/issues/5563)) ([5c17c26](https://github.com/OHIF/Viewers/commit/5c17c262eaa5c1229033cfff4b3e7709e874611f))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.86](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.85...v3.12.0-beta.86) (2025-11-13)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-seg
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.85](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.84...v3.12.0-beta.85) (2025-11-11)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **interpolation:** Auto accept interpolation when the interpolation process is completed. ([#5555](https://github.com/OHIF/Viewers/issues/5555)) ([834ae3c](https://github.com/OHIF/Viewers/commit/834ae3c762ca309187afebdf77bad1ad9a57f03f))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.84](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.83...v3.12.0-beta.84) (2025-11-10)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-seg
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.83](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.82...v3.12.0-beta.83) (2025-11-06)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-seg
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.82](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.81...v3.12.0-beta.82) (2025-11-05)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-seg
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.81](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.80...v3.12.0-beta.81) (2025-11-05)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **SegmentationTools:** [Bug] Changes of brush/eraser radius with hotkey do not reflect on segmentation tool ([#5535](https://github.com/OHIF/Viewers/issues/5535)) ([29bd87c](https://github.com/OHIF/Viewers/commit/29bd87c8c0592a9a1350e687b862f5452627aece))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.80](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.79...v3.12.0-beta.80) (2025-11-05)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-seg
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.79](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.78...v3.12.0-beta.79) (2025-10-30)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-seg
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.78](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.77...v3.12.0-beta.78) (2025-10-29)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-seg
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.77](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.76...v3.12.0-beta.77) (2025-10-28)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **3DSegmentation:** [Bug] The viewports become blank when loading the seg file in advanced layout after closing the seg file from any other advanced layout ([#5505](https://github.com/OHIF/Viewers/issues/5505)) ([76f7d4e](https://github.com/OHIF/Viewers/commit/76f7d4e23aa359a3397b10adf0bf09df3763f190))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.76](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.75...v3.12.0-beta.76) (2025-10-27)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-seg
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.75](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.74...v3.12.0-beta.75) (2025-10-24)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-seg
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.74](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.73...v3.12.0-beta.74) (2025-10-23)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-seg
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.73](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.72...v3.12.0-beta.73) (2025-10-22)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-seg
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.72](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.71...v3.12.0-beta.72) (2025-10-22)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-seg
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.71](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.70...v3.12.0-beta.71) (2025-10-21)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-seg
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.70](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.69...v3.12.0-beta.70) (2025-10-20)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-seg
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.69](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.68...v3.12.0-beta.69) (2025-10-20)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Pass the correct sop uid + frame for rehydration to prevent associating rehydrated measurements with the wrong data ([#5506](https://github.com/OHIF/Viewers/issues/5506)) ([5037802](https://github.com/OHIF/Viewers/commit/503780247ce7228c602a539fd88ea374e7d0d42f)), closes [#2404](https://github.com/OHIF/Viewers/issues/2404)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.68](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.67...v3.12.0-beta.68) (2025-10-18)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **segmentation:** Lock all rehydrated segmentation segments when panelSegmentation.disableEditing is true. ([#5503](https://github.com/OHIF/Viewers/issues/5503)) ([170e860](https://github.com/OHIF/Viewers/commit/170e860aa1b786ab6056cf31076804302a2bc462))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.67](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.66...v3.12.0-beta.67) (2025-10-17)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-seg
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.66](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.65...v3.12.0-beta.66) (2025-10-17)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-seg
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.65](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.64...v3.12.0-beta.65) (2025-10-17)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-seg
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.64](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.63...v3.12.0-beta.64) (2025-10-15)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-seg
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.63](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.62...v3.12.0-beta.63) (2025-10-15)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-seg
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.62](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.61...v3.12.0-beta.62) (2025-10-15)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-seg
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@ohif/extension-cornerstone-dicom-seg",
|
||||
"version": "3.12.0-beta.62",
|
||||
"version": "3.12.0-beta.97",
|
||||
"description": "DICOM SEG read workflow",
|
||||
"author": "OHIF",
|
||||
"license": "MIT",
|
||||
@@ -33,10 +33,10 @@
|
||||
"start": "yarn run dev"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@ohif/core": "3.12.0-beta.62",
|
||||
"@ohif/extension-cornerstone": "3.12.0-beta.62",
|
||||
"@ohif/extension-default": "3.12.0-beta.62",
|
||||
"@ohif/i18n": "3.12.0-beta.62",
|
||||
"@ohif/core": "3.12.0-beta.97",
|
||||
"@ohif/extension-cornerstone": "3.12.0-beta.97",
|
||||
"@ohif/extension-default": "3.12.0-beta.97",
|
||||
"@ohif/i18n": "3.12.0-beta.97",
|
||||
"prop-types": "15.8.1",
|
||||
"react": "18.3.1",
|
||||
"react-dom": "18.3.1",
|
||||
@@ -46,9 +46,9 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": "7.28.2",
|
||||
"@cornerstonejs/adapters": "4.5.5",
|
||||
"@cornerstonejs/core": "4.5.5",
|
||||
"@kitware/vtk.js": "32.12.0",
|
||||
"@cornerstonejs/adapters": "4.12.6",
|
||||
"@cornerstonejs/core": "4.12.6",
|
||||
"@kitware/vtk.js": "34.15.1",
|
||||
"react-color": "2.19.3"
|
||||
}
|
||||
}
|
||||
@@ -1,15 +1,13 @@
|
||||
import dcmjs from 'dcmjs';
|
||||
import { classes, Types } from '@ohif/core';
|
||||
import { classes, Types, utils } from '@ohif/core';
|
||||
import { cache, metaData } from '@cornerstonejs/core';
|
||||
import { segmentation as cornerstoneToolsSegmentation } from '@cornerstonejs/tools';
|
||||
import { adaptersRT, helpers, adaptersSEG } from '@cornerstonejs/adapters';
|
||||
import { createReportDialogPrompt } from '@ohif/extension-default';
|
||||
import { createReportDialogPrompt, useUIStateStore } from '@ohif/extension-default';
|
||||
import { DicomMetadataStore } from '@ohif/core';
|
||||
|
||||
import PROMPT_RESPONSES from '../../default/src/utils/_shared/PROMPT_RESPONSES';
|
||||
|
||||
const { datasetToBlob } = dcmjs.data;
|
||||
|
||||
const getTargetViewport = ({ viewportId, viewportGridService }) => {
|
||||
const { viewports, activeViewportId } = viewportGridService.getState();
|
||||
const targetViewportId = viewportId || activeViewportId;
|
||||
@@ -27,7 +25,7 @@ const {
|
||||
|
||||
const {
|
||||
Cornerstone3D: {
|
||||
RTSS: { generateRTSSFromSegmentations },
|
||||
RTSS: { generateRTSSFromRepresentation },
|
||||
},
|
||||
} = adaptersRT;
|
||||
|
||||
@@ -37,7 +35,7 @@ const commandsModule = ({
|
||||
servicesManager,
|
||||
extensionManager,
|
||||
}: Types.Extensions.ExtensionParams): Types.Extensions.CommandsModule => {
|
||||
const { segmentationService, displaySetService, viewportGridService, toolGroupService } =
|
||||
const { segmentationService, displaySetService, viewportGridService } =
|
||||
servicesManager.services as AppTypes.Services;
|
||||
|
||||
const actions = {
|
||||
@@ -92,6 +90,7 @@ const commandsModule = ({
|
||||
*/
|
||||
generateSegmentation: ({ segmentationId, options = {} }) => {
|
||||
const segmentation = cornerstoneToolsSegmentation.state.getSegmentation(segmentationId);
|
||||
const predecessorImageId = options.predecessorImageId ?? segmentation.predecessorImageId;
|
||||
|
||||
const { imageIds } = segmentation.representationData.Labelmap;
|
||||
|
||||
@@ -173,12 +172,10 @@ const commandsModule = ({
|
||||
labelmap3D.metadata[segmentIndex] = segmentMetadata;
|
||||
});
|
||||
|
||||
const generatedSegmentation = generateSegmentation(
|
||||
referencedImages,
|
||||
labelmap3D,
|
||||
metaData,
|
||||
options
|
||||
);
|
||||
const generatedSegmentation = generateSegmentation(referencedImages, labelmap3D, metaData, {
|
||||
predecessorImageId,
|
||||
...options,
|
||||
});
|
||||
|
||||
return generatedSegmentation;
|
||||
},
|
||||
@@ -212,24 +209,28 @@ const commandsModule = ({
|
||||
* @returns {Object|void} Returns the naturalized report if successfully stored,
|
||||
* otherwise throws an error.
|
||||
*/
|
||||
storeSegmentation: async ({ segmentationId, dataSource }) => {
|
||||
storeSegmentation: async ({ segmentationId, dataSource, modality = 'SEG' }) => {
|
||||
const segmentation = segmentationService.getSegmentation(segmentationId);
|
||||
|
||||
if (!segmentation) {
|
||||
throw new Error('No segmentation found');
|
||||
}
|
||||
|
||||
const { label } = segmentation;
|
||||
const { label, predecessorImageId } = segmentation;
|
||||
const defaultDataSource = dataSource ?? extensionManager.getActiveDataSource()[0];
|
||||
|
||||
const {
|
||||
value: reportName,
|
||||
dataSourceName: selectedDataSource,
|
||||
series,
|
||||
priorSeriesNumber,
|
||||
action,
|
||||
} = await createReportDialogPrompt({
|
||||
servicesManager,
|
||||
extensionManager,
|
||||
predecessorImageId,
|
||||
title: 'Store Segmentation',
|
||||
modality,
|
||||
});
|
||||
|
||||
if (action === PROMPT_RESPONSES.CREATE_REPORT) {
|
||||
@@ -238,12 +239,18 @@ const commandsModule = ({
|
||||
? extensionManager.getDataSources(selectedDataSource)[0]
|
||||
: defaultDataSource;
|
||||
|
||||
const generatedData = actions.generateSegmentation({
|
||||
const args = {
|
||||
segmentationId,
|
||||
options: {
|
||||
SeriesDescription: reportName || label || 'Research Derived Series',
|
||||
SeriesDescription: series ? undefined : reportName || label || 'Contour Series',
|
||||
SeriesNumber: series ? undefined : 1 + priorSeriesNumber,
|
||||
predecessorImageId: series,
|
||||
},
|
||||
});
|
||||
};
|
||||
const generatedDataAsync =
|
||||
(modality === 'SEG' && actions.generateSegmentation(args)) ||
|
||||
(modality === 'RTSTRUCT' && actions.generateContour(args));
|
||||
const generatedData = await generatedDataAsync;
|
||||
|
||||
if (!generatedData || !generatedData.dataset) {
|
||||
throw new Error('Error during segmentation generation');
|
||||
@@ -270,14 +277,9 @@ const commandsModule = ({
|
||||
}
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Converts segmentations into RTSS for download.
|
||||
* This sample function retrieves all segentations and passes to
|
||||
* cornerstone tool adapter to convert to DICOM RTSS format. It then
|
||||
* converts dataset to downloadable blob.
|
||||
*
|
||||
*/
|
||||
downloadRTSS: async ({ segmentationId }) => {
|
||||
|
||||
generateContour: async args => {
|
||||
const { segmentationId, options } = args;
|
||||
const segmentations = segmentationService.getSegmentation(segmentationId);
|
||||
|
||||
// inject colors to the segmentIndex
|
||||
@@ -287,45 +289,54 @@ const commandsModule = ({
|
||||
segment.color = segmentationService.getSegmentColor(
|
||||
firstRepresentation.viewportId,
|
||||
segmentationId,
|
||||
segmentIndex
|
||||
Number(segmentIndex)
|
||||
);
|
||||
});
|
||||
const predecessorImageId = options?.predecessorImageId ?? segmentations.predecessorImageId;
|
||||
const dataset = await generateRTSSFromRepresentation(segmentations, {
|
||||
predecessorImageId,
|
||||
...options,
|
||||
});
|
||||
return { dataset };
|
||||
},
|
||||
|
||||
const RTSS = await generateRTSSFromSegmentations(
|
||||
segmentations,
|
||||
classes.MetadataProvider,
|
||||
DicomMetadataStore
|
||||
);
|
||||
/**
|
||||
* Downloads an RTSS instance from a segmentation or contour
|
||||
* representation.
|
||||
*/
|
||||
downloadRTSS: async args => {
|
||||
const { dataset } = await actions.generateContour(args);
|
||||
const { InstanceNumber: instanceNumber = 1, SeriesInstanceUID: seriesUID } = dataset;
|
||||
|
||||
try {
|
||||
const reportBlob = datasetToBlob(RTSS);
|
||||
|
||||
//Create a URL for the binary.
|
||||
const objectUrl = URL.createObjectURL(reportBlob);
|
||||
window.location.assign(objectUrl);
|
||||
const filename = `rtss-${seriesUID}-${instanceNumber}.dcm`;
|
||||
downloadDICOMData(dataset, filename);
|
||||
} catch (e) {
|
||||
console.warn(e);
|
||||
}
|
||||
},
|
||||
|
||||
toggleActiveSegmentationUtility: ({ itemId: buttonId }) => {
|
||||
const { uiState, setUIState } = useUIStateStore.getState();
|
||||
const isButtonActive = uiState['activeSegmentationUtility'] === buttonId;
|
||||
console.log('toggleActiveSegmentationUtility', isButtonActive, buttonId);
|
||||
// if the button is active, clear the active segmentation utility
|
||||
if (isButtonActive) {
|
||||
setUIState('activeSegmentationUtility', null);
|
||||
} else {
|
||||
setUIState('activeSegmentationUtility', buttonId);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
const definitions = {
|
||||
loadSegmentationsForViewport: {
|
||||
commandFn: actions.loadSegmentationsForViewport,
|
||||
},
|
||||
|
||||
generateSegmentation: {
|
||||
commandFn: actions.generateSegmentation,
|
||||
},
|
||||
downloadSegmentation: {
|
||||
commandFn: actions.downloadSegmentation,
|
||||
},
|
||||
storeSegmentation: {
|
||||
commandFn: actions.storeSegmentation,
|
||||
},
|
||||
downloadRTSS: {
|
||||
commandFn: actions.downloadRTSS,
|
||||
},
|
||||
loadSegmentationsForViewport: actions.loadSegmentationsForViewport,
|
||||
generateSegmentation: actions.generateSegmentation,
|
||||
downloadSegmentation: actions.downloadSegmentation,
|
||||
storeSegmentation: actions.storeSegmentation,
|
||||
downloadRTSS: actions.downloadRTSS,
|
||||
toggleActiveSegmentationUtility: actions.toggleActiveSegmentationUtility,
|
||||
};
|
||||
|
||||
return {
|
||||
|
||||
@@ -0,0 +1,243 @@
|
||||
import React, { useCallback, useEffect, useState } from 'react';
|
||||
import { useRunCommand, useSystem } from '@ohif/core';
|
||||
import { useActiveViewportSegmentationRepresentations } from '@ohif/extension-cornerstone';
|
||||
import {
|
||||
Button,
|
||||
cn,
|
||||
Input,
|
||||
Label,
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
Separator,
|
||||
Switch,
|
||||
Tabs,
|
||||
TabsList,
|
||||
TabsTrigger,
|
||||
} from '@ohif/ui-next';
|
||||
import { Icons } from '@ohif/ui-next';
|
||||
import { contourSegmentation } from '@cornerstonejs/tools/utilities';
|
||||
import { Segment } from '@cornerstonejs/tools/types';
|
||||
|
||||
const { LogicalOperation } = contourSegmentation;
|
||||
const options = [
|
||||
{
|
||||
value: 'merge',
|
||||
logicalOperation: LogicalOperation.Union,
|
||||
label: 'Merge',
|
||||
icon: 'actions-combine-merge',
|
||||
helperIcon: 'helper-combine-merge',
|
||||
},
|
||||
{
|
||||
value: 'intersect',
|
||||
logicalOperation: LogicalOperation.Intersect,
|
||||
label: 'Intersect',
|
||||
icon: 'actions-combine-intersect',
|
||||
helperIcon: 'helper-combine-intersect',
|
||||
},
|
||||
{
|
||||
value: 'subtract',
|
||||
logicalOperation: LogicalOperation.Subtract,
|
||||
label: 'Subtract',
|
||||
icon: 'actions-combine-subtract',
|
||||
helperIcon: 'helper-combine-subtract',
|
||||
},
|
||||
];
|
||||
|
||||
// Shared component for segment selection
|
||||
function SegmentSelector({
|
||||
label,
|
||||
value,
|
||||
onValueChange,
|
||||
segments,
|
||||
placeholder = 'Select a segment',
|
||||
}: {
|
||||
label: string;
|
||||
value: string;
|
||||
onValueChange: (value: string) => void;
|
||||
segments: Segment[];
|
||||
placeholder?: string;
|
||||
}) {
|
||||
return (
|
||||
<div className="flex justify-between gap-6">
|
||||
<div>{label}</div>
|
||||
<Select
|
||||
key={`select-segment-${label}`}
|
||||
onValueChange={onValueChange}
|
||||
value={value}
|
||||
>
|
||||
<SelectTrigger className="overflow-hidden">
|
||||
<SelectValue placeholder={placeholder} />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{segments.map(segment => (
|
||||
<SelectItem
|
||||
key={segment.segmentIndex}
|
||||
value={segment.segmentIndex.toString()}
|
||||
>
|
||||
{segment.label}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function LogicalContourOperationOptions() {
|
||||
const { servicesManager } = useSystem();
|
||||
const { segmentationService } = servicesManager.services;
|
||||
const { segmentationsWithRepresentations } = useActiveViewportSegmentationRepresentations();
|
||||
|
||||
const activeRepresentation = segmentationsWithRepresentations?.find(
|
||||
({ representation }) => representation?.active
|
||||
);
|
||||
|
||||
const segments = activeRepresentation
|
||||
? Object.values(activeRepresentation.segmentation.segments)
|
||||
: [];
|
||||
|
||||
// Calculate the next available segment index
|
||||
const nextSegmentIndex = activeRepresentation
|
||||
? segmentationService.getNextAvailableSegmentIndex(
|
||||
activeRepresentation.segmentation.segmentationId
|
||||
)
|
||||
: 1;
|
||||
|
||||
const activeSegment = segments.find(segment => segment.active);
|
||||
|
||||
const activeSegmentIndex = activeSegment?.segmentIndex || 0;
|
||||
|
||||
const [operation, setOperation] = useState(options[0]);
|
||||
const [segmentA, setSegmentA] = useState<string>(activeSegmentIndex?.toString() || '');
|
||||
const [segmentB, setSegmentB] = useState<string>('');
|
||||
const [createNewSegment, setCreateNewSegment] = useState<boolean>(false);
|
||||
const [newSegmentName, setNewSegmentName] = useState<string>('');
|
||||
|
||||
useEffect(() => {
|
||||
setSegmentA(activeSegmentIndex?.toString() || null);
|
||||
}, [activeSegmentIndex]);
|
||||
|
||||
useEffect(() => {
|
||||
setNewSegmentName(`Segment ${nextSegmentIndex}`);
|
||||
}, [nextSegmentIndex]);
|
||||
|
||||
const runCommand = useRunCommand();
|
||||
|
||||
const applyLogicalContourOperation = useCallback(() => {
|
||||
let resultSegmentIndex = segmentA;
|
||||
if (createNewSegment) {
|
||||
resultSegmentIndex = nextSegmentIndex.toString();
|
||||
runCommand('addSegment', {
|
||||
segmentationId: activeRepresentation.segmentation.segmentationId,
|
||||
config: {
|
||||
label: newSegmentName,
|
||||
segmentIndex: nextSegmentIndex,
|
||||
},
|
||||
});
|
||||
}
|
||||
runCommand('applyLogicalContourOperation', {
|
||||
segmentAInfo: {
|
||||
segmentationId: activeRepresentation.segmentation.segmentationId,
|
||||
segmentIndex: parseInt(segmentA),
|
||||
},
|
||||
segmentBInfo: {
|
||||
segmentationId: activeRepresentation.segmentation.segmentationId,
|
||||
segmentIndex: parseInt(segmentB),
|
||||
},
|
||||
resultSegmentInfo: {
|
||||
segmentationId: activeRepresentation.segmentation.segmentationId,
|
||||
segmentIndex: parseInt(resultSegmentIndex),
|
||||
},
|
||||
logicalOperation: operation.logicalOperation,
|
||||
});
|
||||
}, [
|
||||
activeRepresentation?.segmentation?.segmentationId,
|
||||
createNewSegment,
|
||||
newSegmentName,
|
||||
nextSegmentIndex,
|
||||
operation.logicalOperation,
|
||||
runCommand,
|
||||
segmentA,
|
||||
segmentB,
|
||||
]);
|
||||
|
||||
return (
|
||||
<div className="flex w-[245px] flex-col gap-4">
|
||||
<div className="flex items-start justify-between">
|
||||
<div className="flex w-auto flex-col items-center gap-2 text-base font-normal leading-none">
|
||||
<Tabs value={operation.value}>
|
||||
<TabsList className="inline-flex space-x-1">
|
||||
{options.map(option => {
|
||||
const { value, icon } = option;
|
||||
return (
|
||||
<TabsTrigger
|
||||
value={value}
|
||||
key={`logical-contour-operation-${value}`}
|
||||
onClick={() => setOperation(option)}
|
||||
>
|
||||
<Icons.ByName name={icon}></Icons.ByName>
|
||||
</TabsTrigger>
|
||||
);
|
||||
})}
|
||||
</TabsList>
|
||||
</Tabs>
|
||||
<div>{operation.label}</div>
|
||||
</div>
|
||||
<div className="bg-primary-dark flex h-[62px] w-[88px] items-center justify-center rounded-lg">
|
||||
<Icons.ByName name={operation.helperIcon}></Icons.ByName>
|
||||
</div>
|
||||
</div>
|
||||
<SegmentSelector
|
||||
label="A"
|
||||
value={segmentA}
|
||||
onValueChange={setSegmentA}
|
||||
segments={segments}
|
||||
/>
|
||||
<SegmentSelector
|
||||
label="B"
|
||||
value={segmentB}
|
||||
onValueChange={setSegmentB}
|
||||
segments={segments}
|
||||
/>
|
||||
<div className="flex justify-end pl-[34px]">
|
||||
<Button
|
||||
className="border-primary/60 grow border"
|
||||
variant="ghost"
|
||||
onClick={() => {
|
||||
applyLogicalContourOperation();
|
||||
}}
|
||||
>
|
||||
{operation.label}
|
||||
</Button>
|
||||
</div>
|
||||
<Separator className="bg-input mt-2 h-[1px]" />
|
||||
<div className="flex flex-col gap-2">
|
||||
<div className="flex items-center justify-start gap-2">
|
||||
<Switch
|
||||
id="logical-contour-operations-create-new-segment-switch"
|
||||
onCheckedChange={setCreateNewSegment}
|
||||
></Switch>
|
||||
<Label htmlFor="logical-contour-operations-create-new-segment-switch">
|
||||
Create a new segment
|
||||
</Label>
|
||||
</div>
|
||||
<div className="pl-9">
|
||||
<Input
|
||||
className={cn(createNewSegment ? 'visible' : 'hidden')}
|
||||
disabled={!createNewSegment}
|
||||
id="logical-contour-operations-create-new-segment-input"
|
||||
type="text"
|
||||
placeholder="New segment name"
|
||||
value={newSegmentName}
|
||||
onChange={e => setNewSegmentName(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default LogicalContourOperationOptions;
|
||||
@@ -0,0 +1,71 @@
|
||||
import React, { useState } from 'react';
|
||||
import { Button, Input, Label, Separator } from '@ohif/ui-next';
|
||||
import { useRunCommand } from '@ohif/core';
|
||||
|
||||
function SimplifyContourOptions() {
|
||||
const [areaThreshold, setAreaThreshold] = useState(10);
|
||||
|
||||
const runCommand = useRunCommand();
|
||||
|
||||
return (
|
||||
<div className="flex w-auto w-[252px] flex-col gap-[8px] text-base font-normal leading-none">
|
||||
<div className="flex w-auto flex-col gap-[10px] text-base font-normal leading-none">
|
||||
<div>Fill contour holes</div>
|
||||
<Button
|
||||
className="border-primary/60 border"
|
||||
variant="ghost"
|
||||
onClick={() => {
|
||||
runCommand('removeContourHoles');
|
||||
}}
|
||||
>
|
||||
Fill Holes
|
||||
</Button>
|
||||
<Separator className="bg-input mt-[20px] h-[1px]" />
|
||||
</div>
|
||||
<div className="flex w-auto flex-col gap-[10px] text-base font-normal leading-none">
|
||||
<div>Remove Small Contours</div>
|
||||
<div className="flex items-center gap-2 self-end">
|
||||
<Label
|
||||
htmlFor="simplify-contour-options"
|
||||
className="text-muted-foreground"
|
||||
>
|
||||
Area Threshold
|
||||
</Label>
|
||||
<Input
|
||||
id="simplify-contour-options"
|
||||
className="w-20"
|
||||
type="number"
|
||||
value={areaThreshold}
|
||||
onChange={e => setAreaThreshold(Number(e.target.value))}
|
||||
/>
|
||||
</div>
|
||||
<Button
|
||||
className="border-primary/60 border"
|
||||
variant="ghost"
|
||||
onClick={() => {
|
||||
runCommand('removeSmallContours', {
|
||||
areaThreshold,
|
||||
});
|
||||
}}
|
||||
>
|
||||
Remove Small Contours
|
||||
</Button>
|
||||
<Separator className="bg-input mt-[20px] h-[1px]" />
|
||||
</div>
|
||||
<div className="flex w-auto flex-col gap-[10px] text-base font-normal leading-none">
|
||||
<div>Create New Segment from Holes</div>
|
||||
<Button
|
||||
className="border-primary/60 border"
|
||||
variant="ghost"
|
||||
onClick={() => {
|
||||
runCommand('convertContourHoles');
|
||||
}}
|
||||
>
|
||||
Create New Segment
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default SimplifyContourOptions;
|
||||
@@ -0,0 +1,39 @@
|
||||
import React from 'react';
|
||||
import { Button, Separator } from '@ohif/ui-next';
|
||||
import { useRunCommand } from '@ohif/core';
|
||||
|
||||
function SmoothContoursOptions() {
|
||||
const runCommand = useRunCommand();
|
||||
|
||||
return (
|
||||
<div className="flex w-auto w-[245px] flex-col gap-[8px] text-base font-normal leading-none">
|
||||
<div className="flex w-auto flex-col gap-[10px] text-base font-normal leading-none">
|
||||
<div>Smooth all edges</div>
|
||||
<Button
|
||||
className="border-primary/60 border"
|
||||
variant="ghost"
|
||||
onClick={() => {
|
||||
runCommand('smoothContours');
|
||||
}}
|
||||
>
|
||||
Smooth Edges
|
||||
</Button>
|
||||
<Separator className="bg-input mt-[20px] h-[1px]" />
|
||||
</div>
|
||||
<div className="flex w-auto flex-col gap-[10px] text-base font-normal leading-none">
|
||||
<div>Remove extra points</div>
|
||||
<Button
|
||||
className="border-primary/60 border"
|
||||
variant="ghost"
|
||||
onClick={() => {
|
||||
runCommand('decimateContours');
|
||||
}}
|
||||
>
|
||||
Remove Points
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default SmoothContoursOptions;
|
||||
@@ -16,19 +16,24 @@ function _getDisplaySetsFromSeries(
|
||||
servicesManager: AppTypes.ServicesManager,
|
||||
extensionManager
|
||||
) {
|
||||
const instance = instances[0];
|
||||
utils.sortStudyInstances(instances);
|
||||
|
||||
// Choose the LAST instance in the list as the most recently created one.
|
||||
const instance = instances[instances.length - 1];
|
||||
|
||||
const {
|
||||
StudyInstanceUID,
|
||||
SeriesInstanceUID,
|
||||
SOPInstanceUID,
|
||||
SeriesDescription,
|
||||
SeriesDescription = '',
|
||||
SeriesNumber,
|
||||
SeriesDate,
|
||||
StructureSetDate,
|
||||
SOPClassUID,
|
||||
wadoRoot,
|
||||
wadoUri,
|
||||
wadoUriRoot,
|
||||
imageId: predecessorImageId,
|
||||
} = instance;
|
||||
|
||||
const displaySet = {
|
||||
@@ -38,7 +43,7 @@ function _getDisplaySetsFromSeries(
|
||||
displaySetInstanceUID: utils.guid(),
|
||||
SeriesDescription,
|
||||
SeriesNumber,
|
||||
SeriesDate,
|
||||
SeriesDate: SeriesDate || StructureSetDate || '',
|
||||
SOPInstanceUID,
|
||||
SeriesInstanceUID,
|
||||
StudyInstanceUID,
|
||||
@@ -53,6 +58,7 @@ function _getDisplaySetsFromSeries(
|
||||
segments: {},
|
||||
sopClassUids,
|
||||
instance,
|
||||
predecessorImageId,
|
||||
instances: [instance],
|
||||
wadoRoot,
|
||||
wadoUriRoot,
|
||||
@@ -73,10 +79,16 @@ function _getDisplaySetsFromSeries(
|
||||
displaySet.referencedImages = instance.ReferencedSeriesSequence.ReferencedInstanceSequence;
|
||||
displaySet.referencedSeriesInstanceUID = referencedSeries.SeriesInstanceUID;
|
||||
const { displaySetService } = servicesManager.services;
|
||||
const referencedDisplaySets = displaySetService.getDisplaySetsForSeries(
|
||||
displaySet.referencedSeriesInstanceUID
|
||||
const referencedDisplaySets = displaySetService.getDisplaySetsForReferences(
|
||||
instance.ReferencedSeriesSequence
|
||||
);
|
||||
|
||||
if (referencedDisplaySets?.length > 1) {
|
||||
console.warn(
|
||||
'Segmentation does not currently handle references to multiple series, defaulting to first series'
|
||||
);
|
||||
}
|
||||
|
||||
const referencedDisplaySet = referencedDisplaySets[0];
|
||||
|
||||
if (!referencedDisplaySet) {
|
||||
|
||||
@@ -1,6 +1,34 @@
|
||||
import { utilities as cstUtils } from '@cornerstonejs/tools';
|
||||
import { useUIStateStore } from '@ohif/extension-default';
|
||||
|
||||
import LogicalContourOperationsOptions from './components/LogicalContourOperationsOptions';
|
||||
import SimplifyContourOptions from './components/SimplifyContourOptions';
|
||||
import SmoothContoursOptions from './components/SmoothContoursOptions';
|
||||
|
||||
export function getToolbarModule({ servicesManager }: withAppTypes) {
|
||||
const { segmentationService, toolbarService, toolGroupService } = servicesManager.services;
|
||||
return [
|
||||
{
|
||||
name: 'cornerstone.SimplifyContourOptions',
|
||||
defaultComponent: SimplifyContourOptions,
|
||||
},
|
||||
{
|
||||
name: 'cornerstone.LogicalContourOperationsOptions',
|
||||
defaultComponent: LogicalContourOperationsOptions,
|
||||
},
|
||||
{
|
||||
name: 'cornerstone.SmoothContoursOptions',
|
||||
defaultComponent: SmoothContoursOptions,
|
||||
},
|
||||
{
|
||||
name: 'cornerstone.isActiveSegmentationUtility',
|
||||
evaluate: ({ button }) => {
|
||||
const { uiState } = useUIStateStore.getState();
|
||||
return {
|
||||
isActive: uiState[`activeSegmentationUtility`] === button.id,
|
||||
};
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'evaluate.cornerstone.hasSegmentation',
|
||||
evaluate: ({ viewportId }) => {
|
||||
@@ -10,6 +38,30 @@ export function getToolbarModule({ servicesManager }: withAppTypes) {
|
||||
};
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'evaluate.cornerstone.hasSegmentationOfType',
|
||||
evaluate: ({ viewportId, segmentationRepresentationType }) => {
|
||||
const segmentations = segmentationService.getSegmentationRepresentations(viewportId);
|
||||
|
||||
if (!segmentations?.length) {
|
||||
return {
|
||||
disabled: true,
|
||||
disabledText: 'No segmentations available',
|
||||
};
|
||||
}
|
||||
|
||||
if (
|
||||
!segmentations.some(segmentation =>
|
||||
Boolean(segmentation.type === segmentationRepresentationType)
|
||||
)
|
||||
) {
|
||||
return {
|
||||
disabled: true,
|
||||
disabledText: `No ${segmentationRepresentationType} segmentations available`,
|
||||
};
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'evaluate.cornerstone.segmentation',
|
||||
evaluate: ({ viewportId, button, toolNames, disabledText }) => {
|
||||
@@ -68,5 +120,23 @@ export function getToolbarModule({ servicesManager }: withAppTypes) {
|
||||
};
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'evaluate.cornerstone.segmentation.synchronizeDrawingRadius',
|
||||
evaluate: ({ button, radiusOptionId }) => {
|
||||
const toolGroupIds = toolGroupService.getToolGroupIds();
|
||||
if (!toolGroupIds?.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const toolGroupId of toolGroupIds) {
|
||||
const brushSize = cstUtils.segmentation.getBrushSizeForToolGroup(toolGroupId);
|
||||
|
||||
if (brushSize) {
|
||||
const option = toolbarService.getOptionById(button, radiusOptionId);
|
||||
option.value = brushSize;
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
@@ -134,7 +134,7 @@ function OHIFCornerstoneSEGViewport(props: withAppTypes) {
|
||||
const { unsubscribe } = segmentationService.subscribe(
|
||||
segmentationService.EVENTS.SEGMENTATION_LOADING_COMPLETE,
|
||||
evt => {
|
||||
if (evt.segDisplaySet.displaySetInstanceUID === segDisplaySet.displaySetInstanceUID) {
|
||||
if (evt.segDisplaySet?.displaySetInstanceUID === segDisplaySet?.displaySetInstanceUID) {
|
||||
setSegIsLoading(false);
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,310 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [3.12.0-beta.97](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.96...v3.12.0-beta.97) (2025-12-03)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-sr
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.96](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.95...v3.12.0-beta.96) (2025-12-02)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-sr
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.95](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.94...v3.12.0-beta.95) (2025-12-02)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* sculptor tool fixes ([#5595](https://github.com/OHIF/Viewers/issues/5595)) ([4471416](https://github.com/OHIF/Viewers/commit/4471416c30bf80d9f6766660d103f28c83ba7bb3))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.94](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.93...v3.12.0-beta.94) (2025-12-02)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-sr
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.93](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.92...v3.12.0-beta.93) (2025-11-25)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-sr
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.92](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.91...v3.12.0-beta.92) (2025-11-24)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-sr
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.91](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.90...v3.12.0-beta.91) (2025-11-21)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-sr
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.90](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.89...v3.12.0-beta.90) (2025-11-21)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-sr
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.89](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.88...v3.12.0-beta.89) (2025-11-20)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-sr
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.88](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.87...v3.12.0-beta.88) (2025-11-20)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Remove dead code and fix rectangle roi rehydration ([#5490](https://github.com/OHIF/Viewers/issues/5490)) ([9ca02b4](https://github.com/OHIF/Viewers/commit/9ca02b4071f77600f7dcd87930fecdcf3d1c249f))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.87](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.86...v3.12.0-beta.87) (2025-11-19)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **SegmentationStyle:** Fix inactive contour visibility and styling. ([#5563](https://github.com/OHIF/Viewers/issues/5563)) ([5c17c26](https://github.com/OHIF/Viewers/commit/5c17c262eaa5c1229033cfff4b3e7709e874611f))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.86](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.85...v3.12.0-beta.86) (2025-11-13)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-sr
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.85](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.84...v3.12.0-beta.85) (2025-11-11)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **interpolation:** Auto accept interpolation when the interpolation process is completed. ([#5555](https://github.com/OHIF/Viewers/issues/5555)) ([834ae3c](https://github.com/OHIF/Viewers/commit/834ae3c762ca309187afebdf77bad1ad9a57f03f))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.84](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.83...v3.12.0-beta.84) (2025-11-10)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-sr
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.83](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.82...v3.12.0-beta.83) (2025-11-06)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-sr
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.82](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.81...v3.12.0-beta.82) (2025-11-05)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-sr
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.81](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.80...v3.12.0-beta.81) (2025-11-05)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **SegmentationTools:** [Bug] Changes of brush/eraser radius with hotkey do not reflect on segmentation tool ([#5535](https://github.com/OHIF/Viewers/issues/5535)) ([29bd87c](https://github.com/OHIF/Viewers/commit/29bd87c8c0592a9a1350e687b862f5452627aece))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.80](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.79...v3.12.0-beta.80) (2025-11-05)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-sr
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.79](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.78...v3.12.0-beta.79) (2025-10-30)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-sr
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.78](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.77...v3.12.0-beta.78) (2025-10-29)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-sr
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.77](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.76...v3.12.0-beta.77) (2025-10-28)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **3DSegmentation:** [Bug] The viewports become blank when loading the seg file in advanced layout after closing the seg file from any other advanced layout ([#5505](https://github.com/OHIF/Viewers/issues/5505)) ([76f7d4e](https://github.com/OHIF/Viewers/commit/76f7d4e23aa359a3397b10adf0bf09df3763f190))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.76](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.75...v3.12.0-beta.76) (2025-10-27)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-sr
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.75](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.74...v3.12.0-beta.75) (2025-10-24)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-sr
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.74](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.73...v3.12.0-beta.74) (2025-10-23)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-sr
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.73](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.72...v3.12.0-beta.73) (2025-10-22)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-sr
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.72](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.71...v3.12.0-beta.72) (2025-10-22)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-sr
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.71](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.70...v3.12.0-beta.71) (2025-10-21)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-sr
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.70](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.69...v3.12.0-beta.70) (2025-10-20)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-sr
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.69](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.68...v3.12.0-beta.69) (2025-10-20)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Pass the correct sop uid + frame for rehydration to prevent associating rehydrated measurements with the wrong data ([#5506](https://github.com/OHIF/Viewers/issues/5506)) ([5037802](https://github.com/OHIF/Viewers/commit/503780247ce7228c602a539fd88ea374e7d0d42f)), closes [#2404](https://github.com/OHIF/Viewers/issues/2404)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.68](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.67...v3.12.0-beta.68) (2025-10-18)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **segmentation:** Lock all rehydrated segmentation segments when panelSegmentation.disableEditing is true. ([#5503](https://github.com/OHIF/Viewers/issues/5503)) ([170e860](https://github.com/OHIF/Viewers/commit/170e860aa1b786ab6056cf31076804302a2bc462))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.67](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.66...v3.12.0-beta.67) (2025-10-17)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-sr
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.66](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.65...v3.12.0-beta.66) (2025-10-17)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-sr
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.65](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.64...v3.12.0-beta.65) (2025-10-17)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-sr
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.64](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.63...v3.12.0-beta.64) (2025-10-15)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-sr
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.63](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.62...v3.12.0-beta.63) (2025-10-15)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-sr
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.62](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.61...v3.12.0-beta.62) (2025-10-15)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dicom-sr
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@ohif/extension-cornerstone-dicom-sr",
|
||||
"version": "3.12.0-beta.62",
|
||||
"version": "3.12.0-beta.97",
|
||||
"description": "OHIF extension for an SR Cornerstone Viewport",
|
||||
"author": "OHIF",
|
||||
"license": "MIT",
|
||||
@@ -34,11 +34,11 @@
|
||||
"test:unit:ci": "jest --ci --runInBand --collectCoverage --passWithNoTests"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@ohif/core": "3.12.0-beta.62",
|
||||
"@ohif/extension-cornerstone": "3.12.0-beta.62",
|
||||
"@ohif/extension-measurement-tracking": "3.12.0-beta.62",
|
||||
"@ohif/ui": "3.12.0-beta.62",
|
||||
"dcmjs": "0.43.1",
|
||||
"@ohif/core": "3.12.0-beta.97",
|
||||
"@ohif/extension-cornerstone": "3.12.0-beta.97",
|
||||
"@ohif/extension-measurement-tracking": "3.12.0-beta.97",
|
||||
"@ohif/ui": "3.12.0-beta.97",
|
||||
"dcmjs": "0.45.0",
|
||||
"dicom-parser": "1.8.21",
|
||||
"hammerjs": "2.0.8",
|
||||
"prop-types": "15.8.1",
|
||||
@@ -46,9 +46,9 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": "7.28.2",
|
||||
"@cornerstonejs/adapters": "4.5.5",
|
||||
"@cornerstonejs/core": "4.5.5",
|
||||
"@cornerstonejs/tools": "4.5.5",
|
||||
"@cornerstonejs/adapters": "4.12.6",
|
||||
"@cornerstonejs/core": "4.12.6",
|
||||
"@cornerstonejs/tools": "4.12.6",
|
||||
"classnames": "2.5.1"
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,14 @@
|
||||
import { metaData, utilities } from '@cornerstonejs/core';
|
||||
import { metaData } from '@cornerstonejs/core';
|
||||
|
||||
import OHIF, { DicomMetadataStore } from '@ohif/core';
|
||||
import OHIF, { DicomMetadataStore, utils } from '@ohif/core';
|
||||
import dcmjs from 'dcmjs';
|
||||
import { adaptersSR } from '@cornerstonejs/adapters';
|
||||
|
||||
import getFilteredCornerstoneToolState from './utils/getFilteredCornerstoneToolState';
|
||||
import hydrateStructuredReport from './utils/hydrateStructuredReport';
|
||||
|
||||
const { downloadBlob } = utils;
|
||||
|
||||
const { MeasurementReport } = adaptersSR.Cornerstone3D;
|
||||
const { log } = OHIF;
|
||||
|
||||
@@ -42,8 +44,6 @@ const _generateReport = (measurementData, additionalFindingTypes, options: Optio
|
||||
dataset.SpecificCharacterSet = 'ISO_IR 192';
|
||||
}
|
||||
|
||||
dataset.InstanceNumber = options.InstanceNumber ?? 1;
|
||||
|
||||
return dataset;
|
||||
};
|
||||
|
||||
@@ -85,8 +85,7 @@ const commandsModule = (props: withAppTypes) => {
|
||||
const reportBlob = dcmjs.data.datasetToBlob(srDataset);
|
||||
|
||||
//Create a URL for the binary.
|
||||
const objectUrl = URL.createObjectURL(reportBlob);
|
||||
window.location.assign(objectUrl);
|
||||
downloadBlob(reportBlob, { filename: 'dicom-sr.dcm' });
|
||||
},
|
||||
|
||||
/**
|
||||
|
||||
@@ -100,6 +100,7 @@ function _getDisplaySetsFromSeries(
|
||||
SeriesTime,
|
||||
ConceptNameCodeSequence,
|
||||
SOPClassUID,
|
||||
imageId: predecessorImageId,
|
||||
} = instance;
|
||||
validateSameStudyUID(instance.StudyInstanceUID, instances);
|
||||
|
||||
@@ -128,6 +129,7 @@ function _getDisplaySetsFromSeries(
|
||||
isImagingMeasurementReport,
|
||||
sopClassUids,
|
||||
instance,
|
||||
predecessorImageId,
|
||||
addInstances,
|
||||
label: SeriesDescription || `${i18n.t('Series')} ${SeriesNumber} - ${i18n.t('SR')}`,
|
||||
};
|
||||
@@ -184,6 +186,10 @@ async function _load(
|
||||
srDisplaySet.referencedImages = [];
|
||||
srDisplaySet.measurements = [];
|
||||
}
|
||||
const { predecessorImageId } = srDisplaySet;
|
||||
for (const measurement of srDisplaySet.measurements) {
|
||||
measurement.predecessorImageId = predecessorImageId;
|
||||
}
|
||||
|
||||
const mappings = measurementService.getSourceMappings(
|
||||
CORNERSTONE_3D_TOOLS_SOURCE_NAME,
|
||||
|
||||
@@ -11,16 +11,11 @@ import {
|
||||
RectangleROITool,
|
||||
} from '@cornerstonejs/tools';
|
||||
import { Types } from '@ohif/core';
|
||||
import { Enums as CSExtensionEnums } from '@ohif/extension-cornerstone';
|
||||
|
||||
import DICOMSRDisplayTool from './tools/DICOMSRDisplayTool';
|
||||
import SCOORD3DPointTool from './tools/SCOORD3DPointTool';
|
||||
import SRSCOOR3DProbeMapper from './utils/SRSCOOR3DProbeMapper';
|
||||
import addToolInstance from './utils/addToolInstance';
|
||||
import toolNames from './tools/toolNames';
|
||||
|
||||
const { CORNERSTONE_3D_TOOLS_SOURCE_NAME, CORNERSTONE_3D_TOOLS_SOURCE_VERSION } = CSExtensionEnums;
|
||||
|
||||
/**
|
||||
* @param {object} configuration
|
||||
*/
|
||||
@@ -28,8 +23,6 @@ export default function init({
|
||||
configuration = {},
|
||||
servicesManager,
|
||||
}: Types.Extensions.ExtensionParams): void {
|
||||
const { measurementService } = servicesManager.services;
|
||||
|
||||
addToolInstance(toolNames.DICOMSRDisplay, DICOMSRDisplayTool);
|
||||
addToolInstance(toolNames.SRLength, LengthTool);
|
||||
addToolInstance(toolNames.SRBidirectional, BidirectionalTool);
|
||||
@@ -39,26 +32,10 @@ export default function init({
|
||||
addToolInstance(toolNames.SRAngle, AngleTool);
|
||||
addToolInstance(toolNames.SRPlanarFreehandROI, PlanarFreehandROITool);
|
||||
addToolInstance(toolNames.SRRectangleROI, RectangleROITool);
|
||||
addToolInstance(toolNames.SRSCOORD3DPoint, SCOORD3DPointTool);
|
||||
|
||||
// TODO - fix the SR display of Cobb Angle, as it joins the two lines
|
||||
addToolInstance(toolNames.SRCobbAngle, CobbAngleTool);
|
||||
|
||||
const csTools3DVer1MeasurementSource = measurementService.getSource(
|
||||
CORNERSTONE_3D_TOOLS_SOURCE_NAME,
|
||||
CORNERSTONE_3D_TOOLS_SOURCE_VERSION
|
||||
);
|
||||
|
||||
const { POINT } = measurementService.VALUE_TYPES;
|
||||
|
||||
measurementService.addMapping(
|
||||
csTools3DVer1MeasurementSource,
|
||||
'SRSCOORD3DPoint',
|
||||
POINT,
|
||||
SRSCOOR3DProbeMapper.toAnnotation,
|
||||
SRSCOOR3DProbeMapper.toMeasurement.bind(null, { servicesManager })
|
||||
);
|
||||
|
||||
// Modify annotation tools to use dashed lines on SR
|
||||
const dashedLine = {
|
||||
lineDash: '4,4',
|
||||
|
||||
@@ -1,203 +0,0 @@
|
||||
import { type Types } from '@cornerstonejs/core';
|
||||
import {
|
||||
annotation,
|
||||
drawing,
|
||||
utilities,
|
||||
Types as cs3DToolsTypes,
|
||||
AnnotationDisplayTool,
|
||||
} from '@cornerstonejs/tools';
|
||||
import toolNames from './toolNames';
|
||||
import { Annotation } from '@cornerstonejs/tools/dist/types/types';
|
||||
|
||||
export default class SCOORD3DPointTool extends AnnotationDisplayTool {
|
||||
static toolName = toolNames.SRSCOORD3DPoint;
|
||||
|
||||
constructor(
|
||||
toolProps = {},
|
||||
defaultToolProps = {
|
||||
configuration: {},
|
||||
}
|
||||
) {
|
||||
super(toolProps, defaultToolProps);
|
||||
}
|
||||
|
||||
_getTextBoxLinesFromLabels(labels) {
|
||||
// TODO -> max 5 for now (label + shortAxis + longAxis), need a generic solution for this!
|
||||
|
||||
const labelLength = Math.min(labels.length, 5);
|
||||
const lines = [];
|
||||
|
||||
return lines;
|
||||
}
|
||||
|
||||
// This tool should not inherit from AnnotationTool and we should not need
|
||||
// to add the following lines.
|
||||
isPointNearTool = () => null;
|
||||
getHandleNearImagePoint = () => null;
|
||||
|
||||
renderAnnotation = (enabledElement: Types.IEnabledElement, svgDrawingHelper: any): void => {
|
||||
const { viewport } = enabledElement;
|
||||
const { element } = viewport;
|
||||
|
||||
const annotations = annotation.state.getAnnotations(this.getToolName(), element);
|
||||
|
||||
// Todo: We don't need this anymore, filtering happens in triggerAnnotationRender
|
||||
if (!annotations?.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Filter toolData to only render the data for the active SR.
|
||||
const filteredAnnotations = annotations;
|
||||
if (!viewport._actors?.size) {
|
||||
return;
|
||||
}
|
||||
|
||||
const styleSpecifier: cs3DToolsTypes.AnnotationStyle.StyleSpecifier = {
|
||||
toolGroupId: this.toolGroupId,
|
||||
toolName: this.getToolName(),
|
||||
viewportId: enabledElement.viewport.id,
|
||||
};
|
||||
|
||||
for (let i = 0; i < filteredAnnotations.length; i++) {
|
||||
const annotation = filteredAnnotations[i];
|
||||
|
||||
const annotationUID = annotation.annotationUID;
|
||||
const { renderableData } = annotation.data;
|
||||
const { POINT: points } = renderableData;
|
||||
|
||||
styleSpecifier.annotationUID = annotationUID;
|
||||
|
||||
const lineWidth = this.getStyle('lineWidth', styleSpecifier, annotation);
|
||||
const lineDash = this.getStyle('lineDash', styleSpecifier, annotation);
|
||||
const color = this.getStyle('color', styleSpecifier, annotation);
|
||||
|
||||
const options = {
|
||||
color,
|
||||
lineDash,
|
||||
lineWidth,
|
||||
};
|
||||
|
||||
const point = points[0][0];
|
||||
|
||||
// check if viewport can render it
|
||||
const viewable = viewport.isReferenceViewable(
|
||||
{ FrameOfReferenceUID: annotation.metadata.FrameOfReferenceUID, cameraFocalPoint: point },
|
||||
{ asNearbyProjection: true }
|
||||
);
|
||||
|
||||
if (!viewable) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// render the point
|
||||
const arrowPointCanvas = viewport.worldToCanvas(point);
|
||||
// Todo: configure this
|
||||
const arrowEndCanvas = [arrowPointCanvas[0] + 20, arrowPointCanvas[1] + 20];
|
||||
const canvasCoordinates = [arrowPointCanvas, arrowEndCanvas];
|
||||
|
||||
drawing.drawArrow(
|
||||
svgDrawingHelper,
|
||||
annotationUID,
|
||||
'1',
|
||||
canvasCoordinates[1],
|
||||
canvasCoordinates[0],
|
||||
{
|
||||
color: options.color,
|
||||
width: options.lineWidth,
|
||||
}
|
||||
);
|
||||
|
||||
this.renderTextBox(
|
||||
svgDrawingHelper,
|
||||
viewport,
|
||||
canvasCoordinates,
|
||||
annotation,
|
||||
styleSpecifier,
|
||||
options
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
renderTextBox(
|
||||
svgDrawingHelper,
|
||||
viewport,
|
||||
canvasCoordinates,
|
||||
annotation,
|
||||
styleSpecifier,
|
||||
options = {}
|
||||
) {
|
||||
if (!canvasCoordinates || !annotation) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { annotationUID, data = {} } = annotation;
|
||||
const { labels } = data;
|
||||
|
||||
const textLines = [];
|
||||
|
||||
for (const label of labels) {
|
||||
// make this generic
|
||||
// fix this
|
||||
if (label.label === '363698007') {
|
||||
textLines.push(`Finding Site: ${label.value}`);
|
||||
}
|
||||
}
|
||||
|
||||
const { color } = options;
|
||||
|
||||
const adaptedCanvasCoordinates = canvasCoordinates;
|
||||
// adapt coordinates if there is an adapter
|
||||
const canvasTextBoxCoords = utilities.drawing.getTextBoxCoordsCanvas(adaptedCanvasCoordinates);
|
||||
|
||||
if (!annotation.data?.handles?.textBox?.worldPosition) {
|
||||
annotation.data.handles.textBox.worldPosition = viewport.canvasToWorld(canvasTextBoxCoords);
|
||||
}
|
||||
|
||||
const textBoxPosition = viewport.worldToCanvas(annotation.data.handles.textBox.worldPosition);
|
||||
|
||||
const textBoxUID = '1';
|
||||
const textBoxOptions = this.getLinkedTextBoxStyle(styleSpecifier, annotation);
|
||||
|
||||
const boundingBox = drawing.drawLinkedTextBox(
|
||||
svgDrawingHelper,
|
||||
annotationUID,
|
||||
textBoxUID,
|
||||
textLines,
|
||||
textBoxPosition,
|
||||
canvasCoordinates,
|
||||
{},
|
||||
{
|
||||
...textBoxOptions,
|
||||
color,
|
||||
}
|
||||
);
|
||||
|
||||
const { x: left, y: top, width, height } = boundingBox;
|
||||
|
||||
annotation.data.handles.textBox.worldBoundingBox = {
|
||||
topLeft: viewport.canvasToWorld([left, top]),
|
||||
topRight: viewport.canvasToWorld([left + width, top]),
|
||||
bottomLeft: viewport.canvasToWorld([left, top + height]),
|
||||
bottomRight: viewport.canvasToWorld([left + width, top + height]),
|
||||
};
|
||||
}
|
||||
|
||||
public getLinkedTextBoxStyle(
|
||||
specifications: cs3DToolsTypes.AnnotationStyle.StyleSpecifier,
|
||||
annotation?: Annotation
|
||||
): Record<string, unknown> {
|
||||
// Todo: this function can be used to set different styles for different toolMode
|
||||
// for the textBox.
|
||||
|
||||
return {
|
||||
visibility: this.getStyle('textBoxVisibility', specifications, annotation),
|
||||
fontFamily: this.getStyle('textBoxFontFamily', specifications, annotation),
|
||||
fontSize: this.getStyle('textBoxFontSize', specifications, annotation),
|
||||
color: this.getStyle('textBoxColor', specifications, annotation),
|
||||
shadow: this.getStyle('textBoxShadow', specifications, annotation),
|
||||
background: this.getStyle('textBoxBackground', specifications, annotation),
|
||||
lineWidth: this.getStyle('textBoxLinkLineWidth', specifications, annotation),
|
||||
lineDash: this.getStyle('textBoxLinkLineDash', specifications, annotation),
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,6 @@ const toolNames = {
|
||||
SRCobbAngle: 'SRCobbAngle',
|
||||
SRRectangleROI: 'SRRectangleROI',
|
||||
SRPlanarFreehandROI: 'SRPlanarFreehandROI',
|
||||
SRSCOORD3DPoint: 'SRSCOORD3DPoint',
|
||||
};
|
||||
|
||||
export default toolNames;
|
||||
@@ -1,71 +0,0 @@
|
||||
const SRSCOOR3DProbe = {
|
||||
toAnnotation: measurement => {},
|
||||
|
||||
/**
|
||||
* Maps cornerstone annotation event data to measurement service format.
|
||||
*
|
||||
* @param {Object} cornerstone Cornerstone event data
|
||||
* @return {Measurement} Measurement instance
|
||||
*/
|
||||
toMeasurement: ({ servicesManager, getValueTypeFromToolType }, csToolsEventDetail) => {
|
||||
const { displaySetService } = servicesManager.services;
|
||||
const { annotation } = csToolsEventDetail;
|
||||
const { metadata, data, annotationUID } = annotation;
|
||||
|
||||
if (!metadata || !data) {
|
||||
console.warn('Probe tool: Missing metadata or data');
|
||||
return null;
|
||||
}
|
||||
|
||||
const { toolName, FrameOfReferenceUID } = metadata;
|
||||
const { points } = data.handles;
|
||||
|
||||
const displaySets = displaySetService
|
||||
.getActiveDisplaySets()
|
||||
.filter(ds => ds.FrameOfReferenceUID === FrameOfReferenceUID);
|
||||
const displaySet = displaySets.filter(ds => ds.isReconstructable)[0] || displaySets[0];
|
||||
|
||||
const { StudyInstanceUID: referenceStudyUID, SeriesInstanceUID: referenceSeriesUID } =
|
||||
displaySets[0] || {};
|
||||
|
||||
const displayText = getDisplayText(annotation);
|
||||
return {
|
||||
uid: annotationUID,
|
||||
points,
|
||||
metadata,
|
||||
referenceStudyUID,
|
||||
referenceSeriesUID,
|
||||
displaySetInstanceUID: displaySet?.displaySetInstanceUID,
|
||||
toolName: metadata.toolName,
|
||||
label: data.label,
|
||||
displayText: displayText,
|
||||
data: data.cachedStats,
|
||||
type: getValueTypeFromToolType?.(toolName) ?? null,
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
function getDisplayText(annotation) {
|
||||
const { data } = annotation;
|
||||
|
||||
if (!data) {
|
||||
return [''];
|
||||
}
|
||||
const { labels } = data;
|
||||
|
||||
const displayText = [];
|
||||
|
||||
for (const label of labels) {
|
||||
// make this generic
|
||||
if (label.label === '33636980076') {
|
||||
displayText.push(`Finding Site: ${label.value}`);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
primary: displayText,
|
||||
secondary: [],
|
||||
};
|
||||
}
|
||||
|
||||
export default SRSCOOR3DProbe;
|
||||
@@ -64,33 +64,34 @@ export default function addSRAnnotation({ measurement, imageId = null, frameNumb
|
||||
const { ValueType: valueType, GraphicType: graphicType } = measurement.coords[0];
|
||||
const graphicTypePoints = renderableData[graphicType];
|
||||
|
||||
/** TODO: Read the tool name from the DICOM SR identification type in the future. */
|
||||
/**
|
||||
* TODO: Read the tool name from the DICOM SR identification type in the future.
|
||||
*/
|
||||
let frameOfReferenceUID = null;
|
||||
let planeRestriction = null;
|
||||
|
||||
/**
|
||||
* Store the view reference for use in initial navigation
|
||||
*/
|
||||
if (imageId) {
|
||||
const imagePlaneModule = metaData.get('imagePlaneModule', imageId);
|
||||
frameOfReferenceUID = imagePlaneModule?.frameOfReferenceUID;
|
||||
}
|
||||
|
||||
/**
|
||||
* Store the view reference for use in initial navigation
|
||||
*/
|
||||
if (valueType === 'SCOORD3D') {
|
||||
const adapter = MeasurementReport.getAdapterForTrackingIdentifier(
|
||||
measurement.TrackingIdentifier
|
||||
);
|
||||
if (!adapter) {
|
||||
toolName = toolNames.SRSCOORD3DPoint;
|
||||
}
|
||||
|
||||
// get the ReferencedFrameOfReferenceUID from the measurement
|
||||
frameOfReferenceUID = measurement.coords[0].ReferencedFrameOfReferenceSequence;
|
||||
|
||||
planeRestriction = {
|
||||
FrameOfReferenceUID: frameOfReferenceUID,
|
||||
point: graphicTypePoints[0][0],
|
||||
};
|
||||
}
|
||||
|
||||
// Store the view reference for use in initial navigation
|
||||
/**
|
||||
* Store the view reference for use in initial navigation
|
||||
*/
|
||||
measurement.viewReference = {
|
||||
planeRestriction,
|
||||
FrameOfReferenceUID: frameOfReferenceUID,
|
||||
|
||||
@@ -54,6 +54,11 @@ export default function hydrateStructuredReport(
|
||||
const disableEditing = customizationService.getCustomization('panelMeasurement.disableEditing');
|
||||
|
||||
const displaySet = displaySetService.getDisplaySetByUID(displaySetInstanceUID);
|
||||
const {
|
||||
StudyInstanceUID: studyUID,
|
||||
SeriesInstanceUID: seriesUID,
|
||||
instance: { SOPInstanceUID: sopUID },
|
||||
} = displaySet;
|
||||
|
||||
// TODO -> We should define a strict versioning somewhere.
|
||||
const mappings = measurementService.getSourceMappings(
|
||||
@@ -67,24 +72,16 @@ export default function hydrateStructuredReport(
|
||||
);
|
||||
}
|
||||
|
||||
const instance = DicomMetadataStore.getInstance(
|
||||
displaySet.StudyInstanceUID,
|
||||
displaySet.SeriesInstanceUID,
|
||||
displaySet.SOPInstanceUID
|
||||
);
|
||||
const instance = DicomMetadataStore.getInstance(studyUID, seriesUID, sopUID);
|
||||
|
||||
const sopInstanceUIDToImageId = {};
|
||||
const imageIdsForToolState = {};
|
||||
|
||||
displaySet.measurements.forEach(measurement => {
|
||||
const { ReferencedSOPInstanceUID, imageId, frameNumber } = measurement;
|
||||
const { ReferencedSOPInstanceUID, imageId, frameNumber = 1 } = measurement;
|
||||
const key = `${ReferencedSOPInstanceUID}:${frameNumber}`;
|
||||
|
||||
if (!sopInstanceUIDToImageId[ReferencedSOPInstanceUID]) {
|
||||
sopInstanceUIDToImageId[ReferencedSOPInstanceUID] = imageId;
|
||||
imageIdsForToolState[ReferencedSOPInstanceUID] = [];
|
||||
}
|
||||
if (!imageIdsForToolState[ReferencedSOPInstanceUID][frameNumber]) {
|
||||
imageIdsForToolState[ReferencedSOPInstanceUID][frameNumber] = imageId;
|
||||
if (!sopInstanceUIDToImageId[key]) {
|
||||
sopInstanceUIDToImageId[key] = imageId;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -133,10 +130,8 @@ export default function hydrateStructuredReport(
|
||||
// dcmjs and Cornerstone3D has structural defect in supporting multi-frame
|
||||
// files, and looking up the imageId from sopInstanceUIDToImageId results
|
||||
// in the wrong value.
|
||||
const frameNumber = (toolData.annotation.data && toolData.annotation.data.frameNumber) || 1;
|
||||
const imageId =
|
||||
imageIdsForToolState[toolData.sopInstanceUid][frameNumber] ||
|
||||
sopInstanceUIDToImageId[toolData.sopInstanceUid];
|
||||
const frameNumber = toolData.annotation.data?.frameNumber || 1;
|
||||
const imageId = sopInstanceUIDToImageId[`${toolData.sopInstanceUid}:${frameNumber}`];
|
||||
|
||||
if (!imageIds.includes(imageId)) {
|
||||
imageIds.push(imageId);
|
||||
@@ -175,9 +170,7 @@ export default function hydrateStructuredReport(
|
||||
// files, and looking up the imageId from sopInstanceUIDToImageId results
|
||||
// in the wrong value.
|
||||
const frameNumber = (toolData.annotation.data && toolData.annotation.data.frameNumber) || 1;
|
||||
const imageId =
|
||||
imageIdsForToolState[toolData.sopInstanceUid][frameNumber] ||
|
||||
sopInstanceUIDToImageId[toolData.sopInstanceUid];
|
||||
const imageId = sopInstanceUIDToImageId[`${toolData.sopInstanceUid}:${frameNumber}`];
|
||||
|
||||
if (!imageId) {
|
||||
return getReferenceData3D(toolData, servicesManager);
|
||||
@@ -208,6 +201,7 @@ export default function hydrateStructuredReport(
|
||||
const annotation = {
|
||||
annotationUID: toolData.annotation.annotationUID,
|
||||
data: toolData.annotation.data,
|
||||
predecessorImageId: toolData.predecessorImageId,
|
||||
metadata: {
|
||||
...referenceData,
|
||||
toolName: annotationType,
|
||||
|
||||
@@ -3,6 +3,307 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [3.12.0-beta.97](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.96...v3.12.0-beta.97) (2025-12-03)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dynamic-volume
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.96](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.95...v3.12.0-beta.96) (2025-12-02)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dynamic-volume
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.95](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.94...v3.12.0-beta.95) (2025-12-02)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* sculptor tool fixes ([#5595](https://github.com/OHIF/Viewers/issues/5595)) ([4471416](https://github.com/OHIF/Viewers/commit/4471416c30bf80d9f6766660d103f28c83ba7bb3))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.94](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.93...v3.12.0-beta.94) (2025-12-02)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dynamic-volume
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.93](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.92...v3.12.0-beta.93) (2025-11-25)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dynamic-volume
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.92](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.91...v3.12.0-beta.92) (2025-11-24)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dynamic-volume
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.91](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.90...v3.12.0-beta.91) (2025-11-21)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dynamic-volume
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.90](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.89...v3.12.0-beta.90) (2025-11-21)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dynamic-volume
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.89](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.88...v3.12.0-beta.89) (2025-11-20)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dynamic-volume
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.88](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.87...v3.12.0-beta.88) (2025-11-20)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dynamic-volume
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.87](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.86...v3.12.0-beta.87) (2025-11-19)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **SegmentationStyle:** Fix inactive contour visibility and styling. ([#5563](https://github.com/OHIF/Viewers/issues/5563)) ([5c17c26](https://github.com/OHIF/Viewers/commit/5c17c262eaa5c1229033cfff4b3e7709e874611f))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.86](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.85...v3.12.0-beta.86) (2025-11-13)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dynamic-volume
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.85](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.84...v3.12.0-beta.85) (2025-11-11)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **interpolation:** Auto accept interpolation when the interpolation process is completed. ([#5555](https://github.com/OHIF/Viewers/issues/5555)) ([834ae3c](https://github.com/OHIF/Viewers/commit/834ae3c762ca309187afebdf77bad1ad9a57f03f))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.84](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.83...v3.12.0-beta.84) (2025-11-10)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dynamic-volume
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.83](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.82...v3.12.0-beta.83) (2025-11-06)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dynamic-volume
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.82](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.81...v3.12.0-beta.82) (2025-11-05)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dynamic-volume
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.81](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.80...v3.12.0-beta.81) (2025-11-05)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **SegmentationTools:** [Bug] Changes of brush/eraser radius with hotkey do not reflect on segmentation tool ([#5535](https://github.com/OHIF/Viewers/issues/5535)) ([29bd87c](https://github.com/OHIF/Viewers/commit/29bd87c8c0592a9a1350e687b862f5452627aece))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.80](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.79...v3.12.0-beta.80) (2025-11-05)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dynamic-volume
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.79](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.78...v3.12.0-beta.79) (2025-10-30)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dynamic-volume
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.78](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.77...v3.12.0-beta.78) (2025-10-29)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dynamic-volume
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.77](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.76...v3.12.0-beta.77) (2025-10-28)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **3DSegmentation:** [Bug] The viewports become blank when loading the seg file in advanced layout after closing the seg file from any other advanced layout ([#5505](https://github.com/OHIF/Viewers/issues/5505)) ([76f7d4e](https://github.com/OHIF/Viewers/commit/76f7d4e23aa359a3397b10adf0bf09df3763f190))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.76](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.75...v3.12.0-beta.76) (2025-10-27)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dynamic-volume
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.75](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.74...v3.12.0-beta.75) (2025-10-24)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dynamic-volume
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.74](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.73...v3.12.0-beta.74) (2025-10-23)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dynamic-volume
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.73](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.72...v3.12.0-beta.73) (2025-10-22)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dynamic-volume
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.72](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.71...v3.12.0-beta.72) (2025-10-22)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dynamic-volume
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.71](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.70...v3.12.0-beta.71) (2025-10-21)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dynamic-volume
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.70](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.69...v3.12.0-beta.70) (2025-10-20)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dynamic-volume
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.69](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.68...v3.12.0-beta.69) (2025-10-20)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Pass the correct sop uid + frame for rehydration to prevent associating rehydrated measurements with the wrong data ([#5506](https://github.com/OHIF/Viewers/issues/5506)) ([5037802](https://github.com/OHIF/Viewers/commit/503780247ce7228c602a539fd88ea374e7d0d42f)), closes [#2404](https://github.com/OHIF/Viewers/issues/2404)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.68](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.67...v3.12.0-beta.68) (2025-10-18)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **segmentation:** Lock all rehydrated segmentation segments when panelSegmentation.disableEditing is true. ([#5503](https://github.com/OHIF/Viewers/issues/5503)) ([170e860](https://github.com/OHIF/Viewers/commit/170e860aa1b786ab6056cf31076804302a2bc462))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.67](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.66...v3.12.0-beta.67) (2025-10-17)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dynamic-volume
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.66](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.65...v3.12.0-beta.66) (2025-10-17)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dynamic-volume
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.65](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.64...v3.12.0-beta.65) (2025-10-17)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dynamic-volume
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.64](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.63...v3.12.0-beta.64) (2025-10-15)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dynamic-volume
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.63](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.62...v3.12.0-beta.63) (2025-10-15)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dynamic-volume
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.62](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.61...v3.12.0-beta.62) (2025-10-15)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone-dynamic-volume
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@ohif/extension-cornerstone-dynamic-volume",
|
||||
"version": "3.12.0-beta.62",
|
||||
"version": "3.12.0-beta.97",
|
||||
"description": "OHIF extension for 4D volumes data",
|
||||
"author": "OHIF",
|
||||
"license": "MIT",
|
||||
@@ -29,12 +29,12 @@
|
||||
"test:unit:ci": "jest --ci --runInBand --collectCoverage --passWithNoTests"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@ohif/core": "3.12.0-beta.62",
|
||||
"@ohif/extension-cornerstone": "3.12.0-beta.62",
|
||||
"@ohif/extension-default": "3.12.0-beta.62",
|
||||
"@ohif/i18n": "3.12.0-beta.62",
|
||||
"@ohif/ui": "3.12.0-beta.62",
|
||||
"dcmjs": "0.43.1",
|
||||
"@ohif/core": "3.12.0-beta.97",
|
||||
"@ohif/extension-cornerstone": "3.12.0-beta.97",
|
||||
"@ohif/extension-default": "3.12.0-beta.97",
|
||||
"@ohif/i18n": "3.12.0-beta.97",
|
||||
"@ohif/ui": "3.12.0-beta.97",
|
||||
"dcmjs": "0.45.0",
|
||||
"dicom-parser": "1.8.21",
|
||||
"hammerjs": "2.0.8",
|
||||
"prop-types": "15.8.1",
|
||||
@@ -42,8 +42,8 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": "7.28.2",
|
||||
"@cornerstonejs/core": "4.5.5",
|
||||
"@cornerstonejs/tools": "4.5.5",
|
||||
"@cornerstonejs/core": "4.12.6",
|
||||
"@cornerstonejs/tools": "4.12.6",
|
||||
"classnames": "2.5.1"
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,10 @@
|
||||
import * as importedActions from './actions';
|
||||
import { utilities, Enums } from '@cornerstonejs/tools';
|
||||
import { cache } from '@cornerstonejs/core';
|
||||
import { utils } from '@ohif/core';
|
||||
|
||||
const LABELMAP = Enums.SegmentationRepresentations.Labelmap;
|
||||
import * as importedActions from './actions';
|
||||
|
||||
const { downloadCsv } = utils;
|
||||
|
||||
const commandsModule = ({ commandsManager, servicesManager }: withAppTypes) => {
|
||||
const services = servicesManager.services;
|
||||
@@ -37,7 +39,7 @@ const commandsModule = ({ commandsManager, servicesManager }: withAppTypes) => {
|
||||
});
|
||||
return computedDisplaySets;
|
||||
},
|
||||
exportTimeReportCSV: ({ segmentations, config, options, summaryStats }) => {
|
||||
exportTimeReportCSV: ({ segmentations, summaryStats }) => {
|
||||
const dynamic4DDisplaySet = actions.getDynamic4DDisplaySet();
|
||||
|
||||
const volumeId = dynamic4DDisplaySet?.displaySetInstanceUID;
|
||||
@@ -203,15 +205,7 @@ const commandsModule = ({ commandsManager, servicesManager }: withAppTypes) => {
|
||||
|
||||
// Generate filename and trigger download
|
||||
const filename = `${instance.PatientID}.csv`;
|
||||
const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' });
|
||||
const link = document.createElement('a');
|
||||
const url = URL.createObjectURL(blob);
|
||||
link.setAttribute('href', url);
|
||||
link.setAttribute('download', filename);
|
||||
link.style.visibility = 'hidden';
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
downloadCsv(csvContent, { filename });
|
||||
},
|
||||
swapDynamicWithComputedDisplaySet: ({ displaySet }) => {
|
||||
const computedDisplaySet = displaySet;
|
||||
|
||||
@@ -3,6 +3,328 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [3.12.0-beta.97](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.96...v3.12.0-beta.97) (2025-12-03)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* skip VOI sync for empty viewports ([#5599](https://github.com/OHIF/Viewers/issues/5599)) ([1aa4431](https://github.com/OHIF/Viewers/commit/1aa443112186851855a638ab974a3060c3e28ea0))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.96](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.95...v3.12.0-beta.96) (2025-12-02)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.95](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.94...v3.12.0-beta.95) (2025-12-02)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* sculptor tool fixes ([#5595](https://github.com/OHIF/Viewers/issues/5595)) ([4471416](https://github.com/OHIF/Viewers/commit/4471416c30bf80d9f6766660d103f28c83ba7bb3))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.94](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.93...v3.12.0-beta.94) (2025-12-02)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.93](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.92...v3.12.0-beta.93) (2025-11-25)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.92](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.91...v3.12.0-beta.92) (2025-11-24)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.91](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.90...v3.12.0-beta.91) (2025-11-21)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.90](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.89...v3.12.0-beta.90) (2025-11-21)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **DicomUpload:** Switch to use ui-next components and colors ([#5576](https://github.com/OHIF/Viewers/issues/5576)) ([62d6bf4](https://github.com/OHIF/Viewers/commit/62d6bf437b167ac3e60a0f134869c9f96d199d0f))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.89](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.88...v3.12.0-beta.89) (2025-11-20)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.88](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.87...v3.12.0-beta.88) (2025-11-20)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Remove dead code and fix rectangle roi rehydration ([#5490](https://github.com/OHIF/Viewers/issues/5490)) ([9ca02b4](https://github.com/OHIF/Viewers/commit/9ca02b4071f77600f7dcd87930fecdcf3d1c249f))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.87](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.86...v3.12.0-beta.87) (2025-11-19)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **SegmentationStyle:** Fix inactive contour visibility and styling. ([#5563](https://github.com/OHIF/Viewers/issues/5563)) ([5c17c26](https://github.com/OHIF/Viewers/commit/5c17c262eaa5c1229033cfff4b3e7709e874611f))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.86](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.85...v3.12.0-beta.86) (2025-11-13)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **segmentation:** Allow for manually added label map and contour segmentations to be used as viewport data overlays. ([#5562](https://github.com/OHIF/Viewers/issues/5562)) ([02f6744](https://github.com/OHIF/Viewers/commit/02f6744dab993f16cf2516fd5f657e0743e3fa42))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.85](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.84...v3.12.0-beta.85) (2025-11-11)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **interpolation:** Auto accept interpolation when the interpolation process is completed. ([#5555](https://github.com/OHIF/Viewers/issues/5555)) ([834ae3c](https://github.com/OHIF/Viewers/commit/834ae3c762ca309187afebdf77bad1ad9a57f03f))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.84](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.83...v3.12.0-beta.84) (2025-11-10)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.83](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.82...v3.12.0-beta.83) (2025-11-06)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.82](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.81...v3.12.0-beta.82) (2025-11-05)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **segmentation:** When synchronizing a segmentation representation, use the type of the source viewport segmentation to determine the type of the target viewport segmentation ([#5546](https://github.com/OHIF/Viewers/issues/5546)) ([a0556b0](https://github.com/OHIF/Viewers/commit/a0556b08be98e0bba950ad7b9b575d7b971104ba))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.81](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.80...v3.12.0-beta.81) (2025-11-05)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **SegmentationTools:** [Bug] Changes of brush/eraser radius with hotkey do not reflect on segmentation tool ([#5535](https://github.com/OHIF/Viewers/issues/5535)) ([29bd87c](https://github.com/OHIF/Viewers/commit/29bd87c8c0592a9a1350e687b862f5452627aece))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.80](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.79...v3.12.0-beta.80) (2025-11-05)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.79](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.78...v3.12.0-beta.79) (2025-10-30)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.78](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.77...v3.12.0-beta.78) (2025-10-29)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.77](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.76...v3.12.0-beta.77) (2025-10-28)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **3DSegmentation:** [Bug] The viewports become blank when loading the seg file in advanced layout after closing the seg file from any other advanced layout ([#5505](https://github.com/OHIF/Viewers/issues/5505)) ([76f7d4e](https://github.com/OHIF/Viewers/commit/76f7d4e23aa359a3397b10adf0bf09df3763f190))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.76](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.75...v3.12.0-beta.76) (2025-10-27)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.75](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.74...v3.12.0-beta.75) (2025-10-24)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.74](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.73...v3.12.0-beta.74) (2025-10-23)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **jpeg2000:** Add 16-bit RGB support to JPEG2000 decoder ([#5519](https://github.com/OHIF/Viewers/issues/5519)) ([a154443](https://github.com/OHIF/Viewers/commit/a1544432db42bf77864bb7df2f757cae819a4b4d))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.73](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.72...v3.12.0-beta.73) (2025-10-22)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.72](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.71...v3.12.0-beta.72) (2025-10-22)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.71](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.70...v3.12.0-beta.71) (2025-10-21)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.70](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.69...v3.12.0-beta.70) (2025-10-20)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.69](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.68...v3.12.0-beta.69) (2025-10-20)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Pass the correct sop uid + frame for rehydration to prevent associating rehydrated measurements with the wrong data ([#5506](https://github.com/OHIF/Viewers/issues/5506)) ([5037802](https://github.com/OHIF/Viewers/commit/503780247ce7228c602a539fd88ea374e7d0d42f)), closes [#2404](https://github.com/OHIF/Viewers/issues/2404)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.68](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.67...v3.12.0-beta.68) (2025-10-18)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **segmentation:** Lock all rehydrated segmentation segments when panelSegmentation.disableEditing is true. ([#5503](https://github.com/OHIF/Viewers/issues/5503)) ([170e860](https://github.com/OHIF/Viewers/commit/170e860aa1b786ab6056cf31076804302a2bc462))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.67](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.66...v3.12.0-beta.67) (2025-10-17)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.66](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.65...v3.12.0-beta.66) (2025-10-17)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.65](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.64...v3.12.0-beta.65) (2025-10-17)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.64](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.63...v3.12.0-beta.64) (2025-10-15)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-cornerstone
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.63](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.62...v3.12.0-beta.63) (2025-10-15)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Segmentation not displayed and unable to draw segments in volume viewport after converting Labelmap to Surface ([#5488](https://github.com/OHIF/Viewers/issues/5488)) ([faea09e](https://github.com/OHIF/Viewers/commit/faea09e7ef2667aad5cef1efc2a6516677ca2915))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.62](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.61...v3.12.0-beta.62) (2025-10-15)
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@ohif/extension-cornerstone",
|
||||
"version": "3.12.0-beta.62",
|
||||
"version": "3.12.0-beta.97",
|
||||
"description": "OHIF extension for Cornerstone",
|
||||
"author": "OHIF",
|
||||
"license": "MIT",
|
||||
@@ -38,12 +38,12 @@
|
||||
"peerDependencies": {
|
||||
"@cornerstonejs/codec-charls": "1.2.3",
|
||||
"@cornerstonejs/codec-libjpeg-turbo-8bit": "1.2.2",
|
||||
"@cornerstonejs/codec-openjpeg": "1.2.4",
|
||||
"@cornerstonejs/codec-openjpeg": "1.3.0",
|
||||
"@cornerstonejs/codec-openjph": "2.4.7",
|
||||
"@cornerstonejs/dicom-image-loader": "4.5.5",
|
||||
"@ohif/core": "3.12.0-beta.62",
|
||||
"@ohif/ui": "3.12.0-beta.62",
|
||||
"dcmjs": "0.43.1",
|
||||
"@cornerstonejs/dicom-image-loader": "4.12.6",
|
||||
"@ohif/core": "3.12.0-beta.97",
|
||||
"@ohif/ui": "3.12.0-beta.97",
|
||||
"dcmjs": "0.45.0",
|
||||
"dicom-parser": "1.8.21",
|
||||
"hammerjs": "2.0.8",
|
||||
"prop-types": "15.8.1",
|
||||
@@ -53,14 +53,14 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": "7.28.2",
|
||||
"@cornerstonejs/adapters": "4.5.5",
|
||||
"@cornerstonejs/ai": "4.5.5",
|
||||
"@cornerstonejs/core": "4.5.5",
|
||||
"@cornerstonejs/labelmap-interpolation": "4.5.5",
|
||||
"@cornerstonejs/polymorphic-segmentation": "4.5.5",
|
||||
"@cornerstonejs/tools": "4.5.5",
|
||||
"@cornerstonejs/adapters": "4.12.6",
|
||||
"@cornerstonejs/ai": "4.12.6",
|
||||
"@cornerstonejs/core": "4.12.6",
|
||||
"@cornerstonejs/labelmap-interpolation": "4.12.6",
|
||||
"@cornerstonejs/polymorphic-segmentation": "4.12.6",
|
||||
"@cornerstonejs/tools": "4.12.6",
|
||||
"@itk-wasm/morphological-contour-interpolation": "1.1.0",
|
||||
"@kitware/vtk.js": "32.12.0",
|
||||
"@kitware/vtk.js": "34.15.1",
|
||||
"html2canvas": "1.4.1",
|
||||
"lodash.compact": "3.0.1",
|
||||
"lodash.debounce": "4.0.8",
|
||||
|
||||
@@ -14,13 +14,18 @@ import {
|
||||
utilities as cstUtils,
|
||||
annotation,
|
||||
Types as ToolTypes,
|
||||
SplineContourSegmentationTool,
|
||||
} from '@cornerstonejs/tools';
|
||||
import {
|
||||
SegmentInfo,
|
||||
LogicalOperation,
|
||||
OperatorOptions,
|
||||
} from '@cornerstonejs/tools/utilities/contourSegmentation/logicalOperators';
|
||||
import * as cornerstoneTools from '@cornerstonejs/tools';
|
||||
import * as labelmapInterpolation from '@cornerstonejs/labelmap-interpolation';
|
||||
import { ONNXSegmentationController } from '@cornerstonejs/ai';
|
||||
|
||||
import { Types as OhifTypes, utils } from '@ohif/core';
|
||||
import i18n from '@ohif/i18n';
|
||||
import {
|
||||
callInputDialogAutoComplete,
|
||||
createReportAsync,
|
||||
@@ -33,7 +38,11 @@ import { getFirstAnnotationSelected } from './utils/measurementServiceMappings/u
|
||||
import { getViewportEnabledElement } from './utils/getViewportEnabledElement';
|
||||
import getActiveViewportEnabledElement from './utils/getActiveViewportEnabledElement';
|
||||
import toggleVOISliceSync from './utils/toggleVOISliceSync';
|
||||
import { usePositionPresentationStore, useSegmentationPresentationStore } from './stores';
|
||||
import {
|
||||
usePositionPresentationStore,
|
||||
useSegmentationPresentationStore,
|
||||
useSelectedSegmentationsForViewportStore,
|
||||
} from './stores';
|
||||
import { toolNames } from './initCornerstoneTools';
|
||||
import CornerstoneViewportDownloadForm from './utils/CornerstoneViewportDownloadForm';
|
||||
import { updateSegmentBidirectionalStats } from './utils/updateSegmentationStats';
|
||||
@@ -43,6 +52,11 @@ import { SegmentationRepresentations } from '@cornerstonejs/tools/enums';
|
||||
import { isMeasurementWithinViewport } from './utils/isMeasurementWithinViewport';
|
||||
import { getCenterExtent } from './utils/getCenterExtent';
|
||||
import { EasingFunctionEnum } from './utils/transitions';
|
||||
import { createSegmentationForViewport } from './utils/createSegmentationForViewport';
|
||||
import { utilities as segmentationUtilities } from '@cornerstonejs/tools/segmentation';
|
||||
import i18n from '@ohif/i18n';
|
||||
|
||||
const { add, intersect, subtract, copy } = cstUtils.contourSegmentation;
|
||||
|
||||
const { DefaultHistoryMemo } = csUtils.HistoryMemo;
|
||||
const toggleSyncFunctions = {
|
||||
@@ -106,6 +120,7 @@ function commandsModule({
|
||||
}: OhifTypes.Extensions.ExtensionParams): OhifTypes.Extensions.CommandsModule {
|
||||
const {
|
||||
viewportGridService,
|
||||
toolbarService,
|
||||
toolGroupService,
|
||||
cineService,
|
||||
uiDialogService,
|
||||
@@ -145,6 +160,25 @@ function commandsModule({
|
||||
};
|
||||
}
|
||||
|
||||
function _handleBrushSizeAction(action: 'increase' | 'decrease') {
|
||||
const toolGroupIds = toolGroupService.getToolGroupIds();
|
||||
if (!toolGroupIds?.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const toolGroupId of toolGroupIds) {
|
||||
const brushSize = segmentationUtils.getBrushSizeForToolGroup(toolGroupId);
|
||||
|
||||
const newBrushSize = action === 'increase' ? brushSize + 3 : brushSize - 3;
|
||||
|
||||
if (brushSize) {
|
||||
segmentationUtils.setBrushSizeForToolGroup(toolGroupId, newBrushSize);
|
||||
|
||||
toolbarService.refreshToolbarState({ toolGroupId });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const actions = {
|
||||
jumpToMeasurementViewport: ({ annotationUID, measurement }) => {
|
||||
cornerstoneTools.annotation.selection.setAnnotationSelected(annotationUID, true);
|
||||
@@ -236,16 +270,28 @@ function commandsModule({
|
||||
return;
|
||||
}
|
||||
|
||||
const viewport = cornerstoneViewportService.getCornerstoneViewport(viewportId);
|
||||
|
||||
if (!viewport) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (displaySet.isOverlayDisplaySet) {
|
||||
// update the previously stored segmentationPresentation with the new viewportId
|
||||
// presentation so that when we put the referencedDisplaySet back in the viewport
|
||||
// it will have the correct segmentation representation hydrated
|
||||
|
||||
const segmentationType =
|
||||
// Todo: check if PMAP modality should be handled such as SEG
|
||||
displaySet.Modality !== 'SEG'
|
||||
? SegmentationRepresentations.Contour
|
||||
: viewport.type === CoreEnums.ViewportType.VOLUME_3D
|
||||
? SegmentationRepresentations.Surface
|
||||
: SegmentationRepresentations.Labelmap;
|
||||
|
||||
commandsManager.runCommand('updateStoredSegmentationPresentation', {
|
||||
displaySet,
|
||||
type:
|
||||
displaySet.Modality === 'SEG'
|
||||
? SegmentationRepresentations.Labelmap
|
||||
: SegmentationRepresentations.Contour,
|
||||
type: segmentationType,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -266,10 +312,35 @@ function commandsModule({
|
||||
referencedDisplaySetInstanceUID
|
||||
);
|
||||
storePositionPresentation(referencedDisplaySet);
|
||||
return commandsManager.runCommand('loadSegmentationDisplaySetsForViewport', {
|
||||
|
||||
const results = commandsManager.runCommand('loadSegmentationDisplaySetsForViewport', {
|
||||
viewportId,
|
||||
displaySetInstanceUIDs: [referencedDisplaySet.displaySetInstanceUID],
|
||||
});
|
||||
|
||||
const disableEditing = customizationService.getCustomization(
|
||||
'panelSegmentation.disableEditing'
|
||||
);
|
||||
if (disableEditing) {
|
||||
const segmentationRepresentations = segmentationService.getSegmentationRepresentations(
|
||||
viewportId,
|
||||
{
|
||||
segmentationId: displaySet.displaySetInstanceUID,
|
||||
}
|
||||
);
|
||||
|
||||
segmentationRepresentations.forEach(representation => {
|
||||
const segmentIndices = Object.keys(representation.segments);
|
||||
segmentIndices.forEach(segmentIndex => {
|
||||
segmentationService.setSegmentLocked(
|
||||
representation.segmentationId,
|
||||
parseInt(segmentIndex),
|
||||
true
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
return results;
|
||||
} else if (displaySet.Modality === 'SR') {
|
||||
const results = commandsManager.runCommand('hydrateStructuredReport', {
|
||||
displaySetInstanceUID: displaySet.displaySetInstanceUID,
|
||||
@@ -926,17 +997,21 @@ function commandsModule({
|
||||
}
|
||||
}
|
||||
},
|
||||
setToolActiveToolbar: ({ value, itemId, toolName, toolGroupIds = [] }) => {
|
||||
setToolActiveToolbar: ({ value, itemId, toolName, toolGroupIds = [], bindings }) => {
|
||||
// Sometimes it is passed as value (tools with options), sometimes as itemId (toolbar buttons)
|
||||
toolName = toolName || itemId || value;
|
||||
|
||||
toolGroupIds = toolGroupIds.length ? toolGroupIds : toolGroupService.getToolGroupIds();
|
||||
|
||||
toolGroupIds.forEach(toolGroupId => {
|
||||
actions.setToolActive({ toolName, toolGroupId });
|
||||
actions.setToolActive({ toolName, toolGroupId, bindings });
|
||||
});
|
||||
},
|
||||
setToolActive: ({ toolName, toolGroupId = null }) => {
|
||||
setToolActive: ({
|
||||
toolName,
|
||||
toolGroupId = null,
|
||||
bindings = [{ mouseButton: Enums.MouseBindings.Primary }],
|
||||
}) => {
|
||||
const { viewports } = viewportGridService.getState();
|
||||
|
||||
if (!viewports.size) {
|
||||
@@ -964,11 +1039,7 @@ function commandsModule({
|
||||
|
||||
// Set the new toolName to be active
|
||||
toolGroup.setToolActive(toolName, {
|
||||
bindings: [
|
||||
{
|
||||
mouseButton: Enums.MouseBindings.Primary,
|
||||
},
|
||||
],
|
||||
bindings,
|
||||
});
|
||||
},
|
||||
// capture viewport
|
||||
@@ -1441,48 +1512,24 @@ function commandsModule({
|
||||
* as a segmentation representation to the viewport.
|
||||
*/
|
||||
createLabelmapForViewport: async ({ viewportId, options = {} }) => {
|
||||
const { viewportGridService, displaySetService, segmentationService } =
|
||||
servicesManager.services;
|
||||
const { viewports } = viewportGridService.getState();
|
||||
const targetViewportId = viewportId;
|
||||
|
||||
const viewport = viewports.get(targetViewportId);
|
||||
|
||||
// Todo: add support for multiple display sets
|
||||
const displaySetInstanceUID =
|
||||
options.displaySetInstanceUID || viewport.displaySetInstanceUIDs[0];
|
||||
|
||||
const segs = segmentationService.getSegmentations();
|
||||
|
||||
const label = options.label || `${i18n.t('Tools:Segmentation')} ${segs.length + 1}`;
|
||||
const segmentationId = options.segmentationId || `${csUtils.uuidv4()}`;
|
||||
|
||||
const displaySet = displaySetService.getDisplaySetByUID(displaySetInstanceUID);
|
||||
|
||||
// This will create the segmentation and register it as a display set
|
||||
const generatedSegmentationId = await segmentationService.createLabelmapForDisplaySet(
|
||||
displaySet,
|
||||
{
|
||||
label,
|
||||
segmentationId,
|
||||
segments: options.createInitialSegment
|
||||
? {
|
||||
1: {
|
||||
label: `${i18n.t('Tools:Segment')} 1`,
|
||||
active: true,
|
||||
},
|
||||
}
|
||||
: {},
|
||||
}
|
||||
);
|
||||
|
||||
// Also add the segmentation representation to the viewport
|
||||
await segmentationService.addSegmentationRepresentation(viewportId, {
|
||||
segmentationId,
|
||||
type: Enums.SegmentationRepresentations.Labelmap,
|
||||
return createSegmentationForViewport(servicesManager, {
|
||||
viewportId,
|
||||
options,
|
||||
segmentationType: SegmentationRepresentations.Labelmap,
|
||||
});
|
||||
},
|
||||
/**
|
||||
* Creates a contour for the active viewport
|
||||
*
|
||||
* The created contour will be registered as a display set and also added
|
||||
* as a segmentation representation to the viewport.
|
||||
*/
|
||||
createContourForViewport: async ({ viewportId, options = {} }) => {
|
||||
return createSegmentationForViewport(servicesManager, {
|
||||
viewportId,
|
||||
options,
|
||||
segmentationType: SegmentationRepresentations.Contour,
|
||||
});
|
||||
|
||||
return generatedSegmentationId;
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -1501,9 +1548,9 @@ function commandsModule({
|
||||
* Adds a new segment to a segmentation
|
||||
* @param props.segmentationId - The ID of the segmentation to add the segment to
|
||||
*/
|
||||
addSegmentCommand: ({ segmentationId }) => {
|
||||
addSegmentCommand: ({ segmentationId, config }) => {
|
||||
const { segmentationService } = servicesManager.services;
|
||||
segmentationService.addSegment(segmentationId);
|
||||
segmentationService.addSegment(segmentationId, config);
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -1600,15 +1647,13 @@ function commandsModule({
|
||||
* Stores a segmentation and shows it in the viewport
|
||||
* @param props.segmentationId - The ID of the segmentation to store
|
||||
*/
|
||||
storeSegmentationCommand: async ({ segmentationId }) => {
|
||||
storeSegmentationCommand: async args => {
|
||||
const { segmentationId } = args;
|
||||
const { segmentationService, viewportGridService } = servicesManager.services;
|
||||
|
||||
const displaySetInstanceUIDs = await createReportAsync({
|
||||
servicesManager,
|
||||
getReport: () =>
|
||||
commandsManager.runCommand('storeSegmentation', {
|
||||
segmentationId,
|
||||
}),
|
||||
getReport: () => commandsManager.runCommand('storeSegmentation', args),
|
||||
reportType: 'Segmentation',
|
||||
});
|
||||
|
||||
@@ -1713,6 +1758,16 @@ function commandsModule({
|
||||
segmentationService.setStyle({ type }, { renderFill: value });
|
||||
},
|
||||
|
||||
/**
|
||||
* Sets whether to render fill for inactive segmentations of a segmentation type
|
||||
* @param props.type - The type of segmentation
|
||||
* @param props.value - Whether to render fill for inactive segmentations
|
||||
*/
|
||||
setRenderFillInactiveCommand: ({ type, value }) => {
|
||||
const { segmentationService } = servicesManager.services;
|
||||
segmentationService.setStyle({ type }, { renderFillInactive: value });
|
||||
},
|
||||
|
||||
/**
|
||||
* Sets whether to render outline for a segmentation type
|
||||
* @param props.type - The type of segmentation
|
||||
@@ -1724,13 +1779,36 @@ function commandsModule({
|
||||
},
|
||||
|
||||
/**
|
||||
* Sets the fill alpha for inactive segmentations
|
||||
* Sets whether to render outline for inactive segmentations of a segmentation type
|
||||
* @param props.type - The type of segmentation
|
||||
* @param props.value - Whether to render outline for inactive segmentations
|
||||
*/
|
||||
setRenderOutlineInactiveCommand: ({ type, value }) => {
|
||||
const { segmentationService } = servicesManager.services;
|
||||
segmentationService.setStyle({ type }, { renderOutlineInactive: value });
|
||||
},
|
||||
|
||||
/**
|
||||
* Sets the fill alpha for inactive segmentations.
|
||||
* If no type is provided, the fill alpha for all types will be set.
|
||||
* @param props.type - The type of segmentation
|
||||
* @param props.value - The alpha value to set
|
||||
*/
|
||||
setFillAlphaInactiveCommand: ({ type, value }) => {
|
||||
const { segmentationService } = servicesManager.services;
|
||||
segmentationService.setStyle({ type }, { fillAlphaInactive: value });
|
||||
|
||||
if (type) {
|
||||
segmentationService.setStyle({ type }, { fillAlphaInactive: value });
|
||||
} else {
|
||||
segmentationService.setStyle(
|
||||
{ type: SegmentationRepresentations.Labelmap },
|
||||
{ fillAlphaInactive: value }
|
||||
);
|
||||
segmentationService.setStyle(
|
||||
{ type: SegmentationRepresentations.Contour },
|
||||
{ fillAlphaInactive: value }
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
editSegmentLabel: async ({ segmentationId, segmentIndex }) => {
|
||||
@@ -1998,17 +2076,11 @@ function commandsModule({
|
||||
segmentAI.initViewport(viewport);
|
||||
}
|
||||
},
|
||||
setBrushSize: ({ value, toolNames }) => {
|
||||
setBrushSize: ({ value }) => {
|
||||
const brushSize = Number(value);
|
||||
|
||||
toolGroupService.getToolGroupIds()?.forEach(toolGroupId => {
|
||||
if (toolNames?.length === 0) {
|
||||
segmentationUtils.setBrushSizeForToolGroup(toolGroupId, brushSize);
|
||||
} else {
|
||||
toolNames?.forEach(toolName => {
|
||||
segmentationUtils.setBrushSizeForToolGroup(toolGroupId, brushSize, toolName);
|
||||
});
|
||||
}
|
||||
segmentationUtils.setBrushSizeForToolGroup(toolGroupId, brushSize);
|
||||
});
|
||||
},
|
||||
setThresholdRange: ({
|
||||
@@ -2037,26 +2109,10 @@ function commandsModule({
|
||||
}
|
||||
},
|
||||
increaseBrushSize: () => {
|
||||
const toolGroupIds = toolGroupService.getToolGroupIds();
|
||||
if (!toolGroupIds?.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const toolGroupId of toolGroupIds) {
|
||||
const brushSize = segmentationUtils.getBrushSizeForToolGroup(toolGroupId);
|
||||
segmentationUtils.setBrushSizeForToolGroup(toolGroupId, brushSize + 3);
|
||||
}
|
||||
_handleBrushSizeAction('increase');
|
||||
},
|
||||
decreaseBrushSize: () => {
|
||||
const toolGroupIds = toolGroupService.getToolGroupIds();
|
||||
if (!toolGroupIds?.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const toolGroupId of toolGroupIds) {
|
||||
const brushSize = segmentationUtils.getBrushSizeForToolGroup(toolGroupId);
|
||||
segmentationUtils.setBrushSizeForToolGroup(toolGroupId, brushSize - 3);
|
||||
}
|
||||
_handleBrushSizeAction('decrease');
|
||||
},
|
||||
addNewSegment: () => {
|
||||
const { segmentationService } = servicesManager.services;
|
||||
@@ -2232,6 +2288,205 @@ function commandsModule({
|
||||
deleting,
|
||||
});
|
||||
},
|
||||
activateSelectedSegmentationOfType: ({ segmentationRepresentationType }) => {
|
||||
const { segmentationService, viewportGridService } = servicesManager.services;
|
||||
const activeViewportId = viewportGridService.getActiveViewportId();
|
||||
const { selectedSegmentationsForViewport } =
|
||||
useSelectedSegmentationsForViewportStore.getState();
|
||||
const segmentationId = selectedSegmentationsForViewport[activeViewportId]?.get(
|
||||
segmentationRepresentationType
|
||||
);
|
||||
|
||||
if (!segmentationId) {
|
||||
return;
|
||||
}
|
||||
|
||||
segmentationService.setActiveSegmentation(activeViewportId, segmentationId);
|
||||
},
|
||||
setDynamicCursorSizeForSculptorTool: ({ value: isDynamicCursorSize }) => {
|
||||
const viewportId = viewportGridService.getActiveViewportId();
|
||||
const toolGroup = toolGroupService.getToolGroupForViewport(viewportId);
|
||||
const sculptorToolInstance = toolGroup.getToolInstance(toolNames.SculptorTool);
|
||||
const oldConfiguration = sculptorToolInstance.configuration;
|
||||
|
||||
sculptorToolInstance.configuration = {
|
||||
...oldConfiguration,
|
||||
updateCursorSize: isDynamicCursorSize ? 'dynamic' : '',
|
||||
};
|
||||
},
|
||||
setInterpolationToolConfiguration: ({ value: interpolateContours, toolNames }) => {
|
||||
const viewportId = viewportGridService.getActiveViewportId();
|
||||
const toolGroup = toolGroupService.getToolGroupForViewport(viewportId);
|
||||
|
||||
// Set the interpolation configuration for the active tool.
|
||||
const activeTool = toolGroupService.getActiveToolForViewport(viewportId);
|
||||
const interpolationConfig = {
|
||||
interpolation: {
|
||||
enabled: interpolateContours,
|
||||
},
|
||||
};
|
||||
toolGroup.setToolConfiguration(activeTool, interpolationConfig);
|
||||
|
||||
// Now set the interpolation configuration for the other tools specified.
|
||||
if (toolNames) {
|
||||
Object.values(toolGroup.getToolInstances()).forEach(toolInstance => {
|
||||
if (toolNames?.includes(toolInstance.toolName)) {
|
||||
toolGroup.setToolConfiguration(toolInstance.toolName, interpolationConfig);
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
setSimplifiedSplineForSplineContourSegmentationTool: ({ value: simplifiedSpline }) => {
|
||||
const viewportId = viewportGridService.getActiveViewportId();
|
||||
const toolGroup = toolGroupService.getToolGroupForViewport(viewportId);
|
||||
Object.values(toolGroup.getToolInstances()).forEach(toolInstance => {
|
||||
if (toolInstance instanceof SplineContourSegmentationTool) {
|
||||
const oldConfiguration = toolInstance.configuration;
|
||||
toolInstance.configuration = {
|
||||
...oldConfiguration,
|
||||
simplifiedSpline,
|
||||
};
|
||||
}
|
||||
});
|
||||
},
|
||||
removeSmallContours: ({ areaThreshold: threshold }) => {
|
||||
const viewportId = viewportGridService.getActiveViewportId();
|
||||
const activeSegmentation = segmentationService.getActiveSegmentation(viewportId);
|
||||
const activeSegment = segmentationService.getActiveSegment(viewportId);
|
||||
|
||||
if (!activeSegmentation || !activeSegment) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { removeContourIslands } = segmentationUtilities;
|
||||
removeContourIslands(activeSegmentation.segmentationId, activeSegment.segmentIndex, {
|
||||
threshold,
|
||||
});
|
||||
const renderingEngine = cornerstoneViewportService.getRenderingEngine();
|
||||
renderingEngine.render();
|
||||
},
|
||||
applyLogicalContourOperation: ({
|
||||
segmentAInfo,
|
||||
segmentBInfo,
|
||||
resultSegmentInfo,
|
||||
logicalOperation,
|
||||
}: {
|
||||
segmentAInfo: SegmentInfo;
|
||||
segmentBInfo: SegmentInfo;
|
||||
resultSegmentInfo: OperatorOptions;
|
||||
logicalOperation: LogicalOperation;
|
||||
}) => {
|
||||
switch (logicalOperation) {
|
||||
case LogicalOperation.Union:
|
||||
add(segmentAInfo, segmentBInfo, resultSegmentInfo);
|
||||
break;
|
||||
case LogicalOperation.Intersect:
|
||||
intersect(segmentAInfo, segmentBInfo, resultSegmentInfo);
|
||||
break;
|
||||
case LogicalOperation.Subtract:
|
||||
subtract(segmentAInfo, segmentBInfo, resultSegmentInfo);
|
||||
break;
|
||||
default:
|
||||
throw new Error('Unsupported logical operation');
|
||||
}
|
||||
},
|
||||
copyContourSegment: ({
|
||||
sourceSegmentInfo,
|
||||
targetSegmentInfo,
|
||||
}: {
|
||||
sourceSegmentInfo: SegmentInfo;
|
||||
targetSegmentInfo?: SegmentInfo;
|
||||
}) => {
|
||||
if (!targetSegmentInfo) {
|
||||
targetSegmentInfo = {
|
||||
segmentationId: sourceSegmentInfo.segmentationId,
|
||||
segmentIndex: segmentationService.getNextAvailableSegmentIndex(
|
||||
sourceSegmentInfo.segmentationId
|
||||
),
|
||||
};
|
||||
segmentationService.addSegment(targetSegmentInfo.segmentationId, {
|
||||
segmentIndex: targetSegmentInfo.segmentIndex,
|
||||
});
|
||||
}
|
||||
|
||||
copy(sourceSegmentInfo, targetSegmentInfo);
|
||||
},
|
||||
smoothContours: () => {
|
||||
const viewportId = viewportGridService.getActiveViewportId();
|
||||
const activeSegmentation = segmentationService.getActiveSegmentation(viewportId);
|
||||
const activeSegment = segmentationService.getActiveSegment(viewportId);
|
||||
|
||||
if (!activeSegmentation || !activeSegment) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { smoothContours } = segmentationUtilities;
|
||||
smoothContours(activeSegmentation.segmentationId, activeSegment.segmentIndex);
|
||||
|
||||
const renderingEngine = cornerstoneViewportService.getRenderingEngine();
|
||||
renderingEngine.render();
|
||||
},
|
||||
removeContourHoles: () => {
|
||||
const viewportId = viewportGridService.getActiveViewportId();
|
||||
const activeSegmentation = segmentationService.getActiveSegmentation(viewportId);
|
||||
const activeSegment = segmentationService.getActiveSegment(viewportId);
|
||||
|
||||
if (!activeSegmentation || !activeSegment) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { removeContourHoles } = segmentationUtilities;
|
||||
removeContourHoles(activeSegmentation.segmentationId, activeSegment.segmentIndex);
|
||||
|
||||
const renderingEngine = cornerstoneViewportService.getRenderingEngine();
|
||||
renderingEngine.render();
|
||||
},
|
||||
decimateContours: () => {
|
||||
const viewportId = viewportGridService.getActiveViewportId();
|
||||
const activeSegmentation = segmentationService.getActiveSegmentation(viewportId);
|
||||
const activeSegment = segmentationService.getActiveSegment(viewportId);
|
||||
|
||||
if (!activeSegmentation || !activeSegment) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { decimateContours } = segmentationUtilities;
|
||||
decimateContours(activeSegmentation.segmentationId, activeSegment.segmentIndex);
|
||||
|
||||
const renderingEngine = cornerstoneViewportService.getRenderingEngine();
|
||||
renderingEngine.render();
|
||||
},
|
||||
convertContourHoles: () => {
|
||||
const viewportId = viewportGridService.getActiveViewportId();
|
||||
const activeSegmentation = segmentationService.getActiveSegmentation(viewportId);
|
||||
const activeSegment = segmentationService.getActiveSegment(viewportId);
|
||||
|
||||
if (!activeSegmentation || !activeSegment) {
|
||||
return;
|
||||
}
|
||||
|
||||
const targetSegmentInfo = {
|
||||
segmentationId: activeSegmentation.segmentationId,
|
||||
segmentIndex: segmentationService.getNextAvailableSegmentIndex(
|
||||
activeSegmentation.segmentationId
|
||||
),
|
||||
};
|
||||
|
||||
segmentationService.addSegment(targetSegmentInfo.segmentationId, {
|
||||
segmentIndex: targetSegmentInfo.segmentIndex,
|
||||
});
|
||||
|
||||
const { convertContourHoles } = segmentationUtilities;
|
||||
convertContourHoles(
|
||||
activeSegmentation.segmentationId,
|
||||
activeSegment.segmentIndex,
|
||||
targetSegmentInfo.segmentationId,
|
||||
targetSegmentInfo.segmentIndex
|
||||
);
|
||||
|
||||
const renderingEngine = cornerstoneViewportService.getRenderingEngine();
|
||||
renderingEngine.render();
|
||||
},
|
||||
};
|
||||
|
||||
const definitions = {
|
||||
@@ -2430,6 +2685,9 @@ function commandsModule({
|
||||
createLabelmapForViewport: {
|
||||
commandFn: actions.createLabelmapForViewport,
|
||||
},
|
||||
createContourForViewport: {
|
||||
commandFn: actions.createContourForViewport,
|
||||
},
|
||||
setActiveSegmentation: {
|
||||
commandFn: actions.setActiveSegmentation,
|
||||
},
|
||||
@@ -2481,9 +2739,15 @@ function commandsModule({
|
||||
setRenderFill: {
|
||||
commandFn: actions.setRenderFillCommand,
|
||||
},
|
||||
setRenderFillInactive: {
|
||||
commandFn: actions.setRenderFillInactiveCommand,
|
||||
},
|
||||
setRenderOutline: {
|
||||
commandFn: actions.setRenderOutlineCommand,
|
||||
},
|
||||
setRenderOutlineInactive: {
|
||||
commandFn: actions.setRenderOutlineInactiveCommand,
|
||||
},
|
||||
setFillAlphaInactive: {
|
||||
commandFn: actions.setFillAlphaInactiveCommand,
|
||||
},
|
||||
@@ -2531,6 +2795,18 @@ function commandsModule({
|
||||
toggleSegmentLabel: actions.toggleSegmentLabel,
|
||||
jumpToMeasurementViewport: actions.jumpToMeasurementViewport,
|
||||
initializeSegmentLabelTool: actions.initializeSegmentLabelTool,
|
||||
activateSelectedSegmentationOfType: actions.activateSelectedSegmentationOfType,
|
||||
setDynamicCursorSizeForSculptorTool: actions.setDynamicCursorSizeForSculptorTool,
|
||||
setSimplifiedSplineForSplineContourSegmentationTool:
|
||||
actions.setSimplifiedSplineForSplineContourSegmentationTool,
|
||||
removeSmallContours: actions.removeSmallContours,
|
||||
applyLogicalContourOperation: actions.applyLogicalContourOperation,
|
||||
copyContourSegment: actions.copyContourSegment,
|
||||
smoothContours: actions.smoothContours,
|
||||
removeContourHoles: actions.removeContourHoles,
|
||||
decimateContours: actions.decimateContours,
|
||||
convertContourHoles: actions.convertContourHoles,
|
||||
setInterpolationToolConfiguration: actions.setInterpolationToolConfiguration,
|
||||
};
|
||||
|
||||
return {
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
.dicom-upload-drop-area-border-dash {
|
||||
background-image: repeating-linear-gradient(
|
||||
to right,
|
||||
#7bb2ce 0%,
|
||||
#7bb2ce 50%,
|
||||
transparent 50%,
|
||||
transparent 100%
|
||||
),
|
||||
repeating-linear-gradient(to right, #7bb2ce 0%, #7bb2ce 50%, transparent 50%, transparent 100%),
|
||||
repeating-linear-gradient(to bottom, #7bb2ce 0%, #7bb2ce 50%, transparent 50%, transparent 100%),
|
||||
repeating-linear-gradient(to bottom, #7bb2ce 0%, #7bb2ce 50%, transparent 50%, transparent 100%);
|
||||
background-position:
|
||||
left top,
|
||||
left bottom,
|
||||
left top,
|
||||
right top;
|
||||
background-repeat: repeat-x, repeat-x, repeat-y, repeat-y;
|
||||
background-size:
|
||||
20px 3px,
|
||||
20px 3px,
|
||||
3px 20px,
|
||||
3px 20px;
|
||||
}
|
||||
@@ -5,8 +5,8 @@ import PropTypes from 'prop-types';
|
||||
import classNames from 'classnames';
|
||||
import DicomFileUploader from '../../utils/DicomFileUploader';
|
||||
import DicomUploadProgress from './DicomUploadProgress';
|
||||
import { Button, ButtonEnums } from '@ohif/ui';
|
||||
import './DicomUpload.css';
|
||||
import { Button } from '@ohif/ui-next';
|
||||
// Removed dashed border CSS; using simple 1px solid border with muted foreground color
|
||||
|
||||
type DicomUploadProps = {
|
||||
dataSource;
|
||||
@@ -15,7 +15,8 @@ type DicomUploadProps = {
|
||||
};
|
||||
|
||||
function DicomUpload({ dataSource, onComplete, onStarted }: DicomUploadProps): ReactElement {
|
||||
const baseClassNames = 'min-h-[480px] flex flex-col bg-black select-none';
|
||||
const baseClassNames =
|
||||
'min-h-[375px] flex flex-col bg-black select-none rounded-lg overflow-hidden';
|
||||
const [dicomFileUploaderArr, setDicomFileUploaderArr] = useState([]);
|
||||
|
||||
const onDrop = useCallback(async acceptedFiles => {
|
||||
@@ -34,9 +35,10 @@ function DicomUpload({ dataSource, onComplete, onStarted }: DicomUploadProps): R
|
||||
{({ getRootProps }) => (
|
||||
<div
|
||||
{...getRootProps()}
|
||||
className="dicom-upload-drop-area-border-dash m-5 flex h-full flex-col items-center justify-center"
|
||||
className="m-5 flex h-full flex-col items-center justify-center rounded-2xl border"
|
||||
style={{ borderColor: 'hsl(var(--muted-foreground) / 0.25)' }}
|
||||
>
|
||||
<div className="flex gap-3">
|
||||
<div className="flex gap-2">
|
||||
<Dropzone
|
||||
onDrop={onDrop}
|
||||
noDrag
|
||||
@@ -44,11 +46,16 @@ function DicomUpload({ dataSource, onComplete, onStarted }: DicomUploadProps): R
|
||||
{({ getRootProps, getInputProps }) => (
|
||||
<div {...getRootProps()}>
|
||||
<Button
|
||||
variant="default"
|
||||
size="lg"
|
||||
disabled={false}
|
||||
onClick={() => {}}
|
||||
>
|
||||
{'Add files'}
|
||||
<input {...getInputProps()} />
|
||||
<input
|
||||
{...getInputProps()}
|
||||
style={{ display: 'none' }}
|
||||
/>
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
@@ -60,7 +67,8 @@ function DicomUpload({ dataSource, onComplete, onStarted }: DicomUploadProps): R
|
||||
{({ getRootProps, getInputProps }) => (
|
||||
<div {...getRootProps()}>
|
||||
<Button
|
||||
type={ButtonEnums.type.secondary}
|
||||
variant="secondary"
|
||||
size="lg"
|
||||
disabled={false}
|
||||
onClick={() => {}}
|
||||
>
|
||||
@@ -69,14 +77,15 @@ function DicomUpload({ dataSource, onComplete, onStarted }: DicomUploadProps): R
|
||||
{...getInputProps()}
|
||||
webkitdirectory="true"
|
||||
mozdirectory="true"
|
||||
style={{ display: 'none' }}
|
||||
/>
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</Dropzone>
|
||||
</div>
|
||||
<div className="pt-5">or drag images or folders here</div>
|
||||
<div className="text-aqua-pale pt-3 text-lg">(DICOM files supported)</div>
|
||||
<div className="text-foreground pt-6 text-base">or drag images or folders here</div>
|
||||
<div className="text-muted-foreground pt-1 text-base">(DICOM files supported)</div>
|
||||
</div>
|
||||
)}
|
||||
</Dropzone>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React, { useCallback, useEffect, useRef, useState, ReactElement } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { useSystem } from '@ohif/core';
|
||||
import { Button } from '@ohif/ui';
|
||||
import { Button } from '@ohif/ui-next';
|
||||
import { Icons } from '@ohif/ui-next';
|
||||
import DicomFileUploader, {
|
||||
EVENTS,
|
||||
@@ -276,7 +276,7 @@ function DicomUploadProgress({
|
||||
|
||||
const getNumCompletedAndTimeRemainingComponent = (): ReactElement => {
|
||||
return (
|
||||
<div className="bg-primary-dark flex h-14 items-center px-1 pb-4 text-lg text-white">
|
||||
<div className="bg-muted flex h-14 items-center px-1 pb-4 text-lg text-white">
|
||||
{numFilesCompleted === dicomFileUploaderArr.length ? (
|
||||
<>
|
||||
<span className={NO_WRAP_ELLIPSIS_CLASS_NAMES}>{`${dicomFileUploaderArr.length} ${
|
||||
@@ -308,7 +308,7 @@ function DicomUploadProgress({
|
||||
|
||||
<span
|
||||
className={
|
||||
'text-primary hover:text-primary-lightactive:text-aqua-pale ml-auto cursor-pointer whitespace-nowrap'
|
||||
'text-primary hover:text-highlight active:text-muted-foreground ml-auto cursor-pointer whitespace-nowrap'
|
||||
}
|
||||
onClick={cancelAllUploads}
|
||||
>
|
||||
@@ -337,11 +337,11 @@ function DicomUploadProgress({
|
||||
|
||||
const getPercentCompleteComponent = (): ReactElement => {
|
||||
return (
|
||||
<div className="ohif-scrollbar border-secondary-light overflow-y-scroll border-b px-2">
|
||||
<div className="ohif-scrollbar border-input overflow-y-scroll border-b px-2">
|
||||
<div className="min-h-14 flex w-full items-center p-2.5">
|
||||
{numFilesCompleted === dicomFileUploaderArr.length ? (
|
||||
<>
|
||||
<div className="text-primary-light text-xl">
|
||||
<div className="text-highlight text-xl">
|
||||
{numFails > 0
|
||||
? `Completed with ${numFails} ${numFails > 1 ? 'errors' : 'error'}!`
|
||||
: 'Completed!'}
|
||||
@@ -359,7 +359,7 @@ function DicomUploadProgress({
|
||||
></ProgressLoadingBar>
|
||||
</div>
|
||||
<div className="ml-1 flex w-24 items-center">
|
||||
<div className="w-10 text-right text-foreground">{`${getPercentCompleteRounded()}%`}</div>
|
||||
<div className="text-foreground w-10 text-right">{`${getPercentCompleteRounded()}%`}</div>
|
||||
{getShowFailedOnlyIconComponent()}
|
||||
</div>
|
||||
</>
|
||||
|
||||
@@ -56,7 +56,7 @@ const DicomUploadProgressItem = memo(
|
||||
return (
|
||||
<Icons.ByName
|
||||
name="status-tracked"
|
||||
className="text-primary-light"
|
||||
className="text-highlight"
|
||||
/>
|
||||
);
|
||||
case UploadStatus.InProgress:
|
||||
@@ -71,7 +71,7 @@ const DicomUploadProgressItem = memo(
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="min-h-14 border-secondary-light flex w-full items-center overflow-hidden border-b p-2.5 text-lg">
|
||||
<div className="min-h-14 border-input flex w-full items-center overflow-hidden border-b p-2.5 text-lg">
|
||||
<div className="self-top flex w-0 shrink grow flex-col gap-1">
|
||||
<div className="flex gap-4">
|
||||
<div className="flex w-6 shrink-0 items-center justify-center">{getStatusIcon()}</div>
|
||||
|
||||
@@ -0,0 +1,108 @@
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import {
|
||||
DropdownMenuSub,
|
||||
DropdownMenuSubTrigger,
|
||||
DropdownMenuPortal,
|
||||
DropdownMenuSubContent,
|
||||
DropdownMenuLabel,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuSeparator,
|
||||
Icons,
|
||||
} from '@ohif/ui-next';
|
||||
|
||||
import { SegmentationRepresentations } from '@cornerstonejs/tools/enums';
|
||||
|
||||
interface ExportSegmentationSubMenuItemProps {
|
||||
segmentationId: string;
|
||||
segmentationRepresentationType: string;
|
||||
allowExport: boolean;
|
||||
actions: {
|
||||
storeSegmentation: (segmentationId: string, modality?: string) => Promise<unknown>;
|
||||
onSegmentationDownloadRTSS: (segmentationId: string) => void;
|
||||
onSegmentationDownload: (segmentationId: string) => void;
|
||||
downloadCSVSegmentationReport: (segmentationId: string) => void;
|
||||
};
|
||||
}
|
||||
|
||||
export const ExportSegmentationSubMenuItem: React.FC<ExportSegmentationSubMenuItemProps> = ({
|
||||
segmentationId,
|
||||
segmentationRepresentationType,
|
||||
allowExport,
|
||||
actions,
|
||||
}) => {
|
||||
const { t } = useTranslation('SegmentationTable');
|
||||
|
||||
return (
|
||||
<DropdownMenuSub>
|
||||
<DropdownMenuSubTrigger className="pl-1">
|
||||
<Icons.Export className="text-foreground" />
|
||||
<span className="pl-2">{t('Download & Export')}</span>
|
||||
</DropdownMenuSubTrigger>
|
||||
<DropdownMenuPortal>
|
||||
<DropdownMenuSubContent>
|
||||
<DropdownMenuLabel className="flex items-center pl-0">
|
||||
<Icons.Download className="h-5 w-5" />
|
||||
<span className="pl-1">{t('Download')}</span>
|
||||
</DropdownMenuLabel>
|
||||
{segmentationRepresentationType === SegmentationRepresentations.Labelmap && (
|
||||
<DropdownMenuItem
|
||||
onClick={e => {
|
||||
e.preventDefault();
|
||||
actions.downloadCSVSegmentationReport(segmentationId);
|
||||
}}
|
||||
disabled={!allowExport}
|
||||
>
|
||||
{t('CSV Report')}
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
{segmentationRepresentationType === SegmentationRepresentations.Labelmap && (
|
||||
<DropdownMenuItem
|
||||
onClick={e => {
|
||||
e.preventDefault();
|
||||
actions.onSegmentationDownload(segmentationId);
|
||||
}}
|
||||
disabled={!allowExport}
|
||||
>
|
||||
{t('DICOM SEG')}
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
<DropdownMenuItem
|
||||
onClick={e => {
|
||||
e.preventDefault();
|
||||
actions.onSegmentationDownloadRTSS(segmentationId);
|
||||
}}
|
||||
disabled={!allowExport}
|
||||
>
|
||||
{t('DICOM RTSS')}
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuLabel className="flex items-center pl-0">
|
||||
<Icons.Export className="h-5 w-5" />
|
||||
<span className="pl-1 pt-1">{t('Export')}</span>
|
||||
</DropdownMenuLabel>
|
||||
{segmentationRepresentationType === SegmentationRepresentations.Labelmap && (
|
||||
<DropdownMenuItem
|
||||
onClick={e => {
|
||||
e.preventDefault();
|
||||
actions.storeSegmentation(segmentationId, 'SEG');
|
||||
}}
|
||||
disabled={!allowExport}
|
||||
>
|
||||
{t('DICOM SEG')}
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
<DropdownMenuItem
|
||||
onClick={e => {
|
||||
e.preventDefault();
|
||||
actions.storeSegmentation(segmentationId, 'RTSTRUCT');
|
||||
}}
|
||||
disabled={!allowExport}
|
||||
>
|
||||
{t('DICOM RTSS')}
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuSubContent>
|
||||
</DropdownMenuPortal>
|
||||
</DropdownMenuSub>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,69 @@
|
||||
import React, { useState } from 'react';
|
||||
import { Switch } from '@ohif/ui-next';
|
||||
import { useSystem } from '@ohif/core';
|
||||
|
||||
export default function SegmentationToolConfig() {
|
||||
const { commandsManager } = useSystem();
|
||||
|
||||
// Get initial states based on current configuration
|
||||
const [previewEdits, setPreviewEdits] = useState(false);
|
||||
const [segmentLabelEnabled, setSegmentLabelEnabled] = useState(false);
|
||||
const [toggleSegmentEnabled, setToggleSegmentEnabled] = useState(false);
|
||||
const [useCenterAsSegmentIndex, setUseCenterAsSegmentIndex] = useState(false);
|
||||
|
||||
const handlePreviewEditsChange = checked => {
|
||||
setPreviewEdits(checked);
|
||||
commandsManager.run('toggleSegmentPreviewEdit', { toggle: checked });
|
||||
};
|
||||
|
||||
const handleToggleSegmentEnabledChange = checked => {
|
||||
setToggleSegmentEnabled(checked);
|
||||
commandsManager.run('toggleSegmentSelect', { toggle: checked });
|
||||
};
|
||||
|
||||
const handleUseCenterAsSegmentIndexChange = checked => {
|
||||
setUseCenterAsSegmentIndex(checked);
|
||||
commandsManager.run('toggleUseCenterSegmentIndex', { toggle: checked });
|
||||
};
|
||||
|
||||
const handleSegmentLabelEnabledChange = checked => {
|
||||
setSegmentLabelEnabled(checked);
|
||||
commandsManager.run('toggleSegmentLabel', { enabled: checked });
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="bg-muted flex flex-col gap-2 border-b border-b-[2px] border-black px-2 py-3">
|
||||
<div className="flex items-center gap-2">
|
||||
<Switch
|
||||
checked={previewEdits}
|
||||
onCheckedChange={handlePreviewEditsChange}
|
||||
/>
|
||||
<span className="text-foreground text-base">Preview edits before creating</span>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-2">
|
||||
<Switch
|
||||
checked={useCenterAsSegmentIndex}
|
||||
onCheckedChange={handleUseCenterAsSegmentIndexChange}
|
||||
/>
|
||||
<span className="text-foreground text-base">Use center as segment index</span>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-2">
|
||||
<Switch
|
||||
checked={toggleSegmentEnabled}
|
||||
onCheckedChange={handleToggleSegmentEnabledChange}
|
||||
/>
|
||||
<span className="text-foreground text-base">Hover on segment border to activate</span>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-2">
|
||||
<Switch
|
||||
checked={segmentLabelEnabled}
|
||||
onCheckedChange={handleSegmentLabelEnabledChange}
|
||||
/>
|
||||
<span className="text-foreground text-base">Show segment name on hover</span>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
import React, { useCallback } from 'react';
|
||||
import { cn, ToolButton } from '@ohif/ui-next';
|
||||
import { useUIStateStore } from '@ohif/extension-default';
|
||||
|
||||
interface SegmentationUtilityButtonProps {
|
||||
className?: string;
|
||||
isActive?: boolean;
|
||||
id: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* A button that represents a segmentation utility.
|
||||
* It is implicitly the PopoverTrigger for the options Popover panel that is
|
||||
* opened in the PanelSegmentation component. It in essence is the PopoverTrigger
|
||||
* because typically this button has the associated options to display in the Popover.
|
||||
* Furthermore it also typically has a command for toggling the activeSegmentationUtility
|
||||
* value in the UIStateStore that triggers the fetching of its options.
|
||||
|
||||
* @param props - The props for the button.
|
||||
* @param props.className - The class name for the button.
|
||||
* @param props.isActive - Whether the button is active
|
||||
* @param props.id - The id of the button.
|
||||
*/
|
||||
function SegmentationUtilityButton(props: SegmentationUtilityButtonProps) {
|
||||
const { className, isActive, id } = props;
|
||||
|
||||
const activeSegmentationUtility = useUIStateStore(
|
||||
store => store.uiState.activeSegmentationUtility
|
||||
);
|
||||
|
||||
const toolButtonClassName = cn(
|
||||
'w-7 h-7 text-primary hover:text-primary hover:!bg-primary/30',
|
||||
className,
|
||||
isActive && 'bg-primary/30'
|
||||
);
|
||||
|
||||
const handleMouseDownCapture = useCallback(
|
||||
event => {
|
||||
if (activeSegmentationUtility === id) {
|
||||
// If this active button is clicked, prevent the default Popover
|
||||
// behaviour of closing the Popover on a pointer/mouse down.
|
||||
// Not doing this will cause the Popover to close and then reopen again.
|
||||
// Why? Because propagating this event will cause PanelSegmentation to
|
||||
// close the Popover by clearing the activeSegmentationUtility. Then
|
||||
// this button will set the activeSegmentationUtility again and the
|
||||
// Popover will reopen.
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
}
|
||||
},
|
||||
[activeSegmentationUtility, id]
|
||||
);
|
||||
|
||||
return (
|
||||
<div onPointerDownCapture={handleMouseDownCapture}>
|
||||
<ToolButton
|
||||
{...props}
|
||||
className={toolButtonClassName}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default SegmentationUtilityButton;
|
||||
@@ -3,17 +3,14 @@ import {
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuLabel,
|
||||
DropdownMenuPortal,
|
||||
DropdownMenuSeparator,
|
||||
DropdownMenuSub,
|
||||
DropdownMenuSubTrigger,
|
||||
DropdownMenuSubContent,
|
||||
Icons,
|
||||
useSegmentationTableContext,
|
||||
useSegmentationExpanded,
|
||||
} from '@ohif/ui-next';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useSystem } from '@ohif/core/src';
|
||||
import { ExportSegmentationSubMenuItem } from '../components/ExportSegmentationSubMenuItem';
|
||||
|
||||
/**
|
||||
* Custom dropdown menu component for segmentation panel that uses context for data
|
||||
@@ -29,6 +26,8 @@ export const CustomDropdownMenuContent = () => {
|
||||
exportOptions,
|
||||
activeSegmentation,
|
||||
activeSegmentationId,
|
||||
segmentationRepresentationType,
|
||||
disableEditing,
|
||||
} = useSegmentationTableContext('CustomDropdownMenu');
|
||||
|
||||
// Try to get segmentation data from expanded context first, fall back to table context
|
||||
@@ -47,21 +46,21 @@ export const CustomDropdownMenuContent = () => {
|
||||
segmentationId = activeSegmentationId;
|
||||
}
|
||||
|
||||
// Determine if export is allowed for this segmentation
|
||||
if (exportOptions && segmentationId) {
|
||||
const exportOption = exportOptions.find(opt => opt.segmentationId === segmentationId);
|
||||
allowExport = exportOption?.isExportable || false;
|
||||
}
|
||||
|
||||
if (!segmentation || !segmentationId) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Determine if export is allowed for this segmentation
|
||||
if (exportOptions) {
|
||||
const exportOption = exportOptions.find(opt => opt.segmentationId === segmentationId);
|
||||
allowExport = exportOption?.isExportable || false;
|
||||
}
|
||||
|
||||
const actions = {
|
||||
storeSegmentation: async segmentationId => {
|
||||
storeSegmentation: async (segmentationId, modality = 'SEG') => {
|
||||
commandsManager.run({
|
||||
commandName: 'storeSegmentation',
|
||||
commandOptions: { segmentationId },
|
||||
commandOptions: { segmentationId, modality },
|
||||
context: 'CORNERSTONE',
|
||||
});
|
||||
},
|
||||
@@ -78,10 +77,14 @@ export const CustomDropdownMenuContent = () => {
|
||||
|
||||
return (
|
||||
<DropdownMenuContent align="start">
|
||||
<DropdownMenuItem onClick={() => onSegmentationAdd(segmentationId)}>
|
||||
<Icons.Add className="text-foreground" />
|
||||
<span className="pl-2">{t('Create New Segmentation')}</span>
|
||||
</DropdownMenuItem>
|
||||
{!disableEditing && (
|
||||
<DropdownMenuItem
|
||||
onClick={() => onSegmentationAdd({ segmentationId, segmentationRepresentationType })}
|
||||
>
|
||||
<Icons.Add className="text-foreground" />
|
||||
<span className="pl-2">{t('Create New Segmentation')}</span>
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuLabel>{t('Manage Current Segmentation')}</DropdownMenuLabel>
|
||||
<DropdownMenuItem onClick={() => onSegmentationRemoveFromViewport(segmentationId)}>
|
||||
@@ -92,61 +95,12 @@ export const CustomDropdownMenuContent = () => {
|
||||
<Icons.Rename className="text-foreground" />
|
||||
<span className="pl-2">{t('Rename')}</span>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuSub>
|
||||
<DropdownMenuSubTrigger className="pl-1">
|
||||
<Icons.Export className="text-foreground" />
|
||||
<span className="pl-2">{t('Download & Export')}</span>
|
||||
</DropdownMenuSubTrigger>
|
||||
<DropdownMenuPortal>
|
||||
<DropdownMenuSubContent>
|
||||
<DropdownMenuLabel className="flex items-center pl-0">
|
||||
<Icons.Download className="h-5 w-5" />
|
||||
<span className="pl-1">{t('Download')}</span>
|
||||
</DropdownMenuLabel>
|
||||
<DropdownMenuItem
|
||||
onClick={e => {
|
||||
e.preventDefault();
|
||||
actions.downloadCSVSegmentationReport(segmentationId);
|
||||
}}
|
||||
disabled={!allowExport}
|
||||
>
|
||||
{t('CSV Report')}
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem
|
||||
onClick={e => {
|
||||
e.preventDefault();
|
||||
actions.onSegmentationDownload(segmentationId);
|
||||
}}
|
||||
disabled={!allowExport}
|
||||
>
|
||||
{t('DICOM SEG')}
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem
|
||||
onClick={e => {
|
||||
e.preventDefault();
|
||||
actions.onSegmentationDownloadRTSS(segmentationId);
|
||||
}}
|
||||
disabled={!allowExport}
|
||||
>
|
||||
{t('DICOM RTSS')}
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuLabel className="flex items-center pl-0">
|
||||
<Icons.Export className="h-5 w-5" />
|
||||
<span className="pl-1 pt-1">{t('Export')}</span>
|
||||
</DropdownMenuLabel>
|
||||
<DropdownMenuItem
|
||||
onClick={e => {
|
||||
e.preventDefault();
|
||||
actions.storeSegmentation(segmentationId);
|
||||
}}
|
||||
disabled={!allowExport}
|
||||
>
|
||||
{t('DICOM SEG')}
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuSubContent>
|
||||
</DropdownMenuPortal>
|
||||
</DropdownMenuSub>
|
||||
<ExportSegmentationSubMenuItem
|
||||
segmentationId={segmentationId}
|
||||
segmentationRepresentationType={segmentationRepresentationType}
|
||||
allowExport={allowExport}
|
||||
actions={actions}
|
||||
/>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItem onClick={() => onSegmentationDelete(segmentationId)}>
|
||||
<Icons.Delete className="text-red-600" />
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
import { CinePlayer } from '@ohif/ui-next';
|
||||
import DicomUpload from '../components/DicomUpload/DicomUpload';
|
||||
|
||||
// Provide a wider default container for the DICOM Upload modal, without
|
||||
// affecting other dialogs. We attach a static property that WorkList reads.
|
||||
const DicomUploadWithSize = Object.assign(DicomUpload, {
|
||||
containerClassName: 'max-w-3xl',
|
||||
});
|
||||
|
||||
export default {
|
||||
cinePlayer: CinePlayer,
|
||||
autoCineModalities: ['OT', 'US'],
|
||||
@@ -11,6 +17,6 @@ export default {
|
||||
onBeforeDicomStore: ({ dicomDict, measurementData, naturalizedReport }) => {
|
||||
return dicomDict;
|
||||
},
|
||||
dicomUploadComponent: DicomUpload,
|
||||
dicomUploadComponent: DicomUploadWithSize,
|
||||
codingValues: {},
|
||||
};
|
||||
@@ -1,18 +1,94 @@
|
||||
import { CustomDropdownMenuContent } from './CustomDropdownMenuContent';
|
||||
import { CustomSegmentStatisticsHeader } from './CustomSegmentStatisticsHeader';
|
||||
import React, { useState } from 'react';
|
||||
import { Switch } from '@ohif/ui-next';
|
||||
import SegmentationToolConfig from '../components/SegmentationToolConfig';
|
||||
import React from 'react';
|
||||
import { SegmentationRepresentations } from '@cornerstonejs/tools/enums';
|
||||
|
||||
export default function getSegmentationPanelCustomization({ commandsManager, servicesManager }) {
|
||||
const { segmentationService } = servicesManager.services;
|
||||
|
||||
let contourRenderFillChangedGlobally = false;
|
||||
|
||||
// Listen to when the global CONTOUR type renderFill style property is changed.
|
||||
const { unsubscribe } = segmentationService.subscribe(
|
||||
segmentationService.EVENTS.SEGMENTATION_STYLE_MODIFIED,
|
||||
({ specifier, style }) => {
|
||||
if (
|
||||
specifier.type === SegmentationRepresentations.Contour &&
|
||||
specifier.segmentationId == null &&
|
||||
specifier.viewportId == null &&
|
||||
style.renderFill != null
|
||||
) {
|
||||
unsubscribe();
|
||||
contourRenderFillChangedGlobally = true;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
return {
|
||||
'panelSegmentation.customDropdownMenuContent': CustomDropdownMenuContent,
|
||||
'panelSegmentation.customSegmentStatisticsHeader': CustomSegmentStatisticsHeader,
|
||||
'panelSegmentation.disableEditing': false,
|
||||
'panelSegmentation.showAddSegment': true,
|
||||
'panelSegmentation.onSegmentationAdd': () => {
|
||||
'panelSegmentation.onSegmentationAdd': async ({
|
||||
segmentationRepresentationType = SegmentationRepresentations.Labelmap,
|
||||
}) => {
|
||||
const { viewportGridService } = servicesManager.services;
|
||||
const viewportId = viewportGridService.getState().activeViewportId;
|
||||
commandsManager.run('createLabelmapForViewport', { viewportId });
|
||||
if (segmentationRepresentationType === SegmentationRepresentations.Labelmap) {
|
||||
commandsManager.run('createLabelmapForViewport', { viewportId });
|
||||
} else if (segmentationRepresentationType === SegmentationRepresentations.Contour) {
|
||||
const segmentationId = await commandsManager.run('createContourForViewport', {
|
||||
viewportId,
|
||||
});
|
||||
// Override the default (i.e. hydrated RTSTRUCT) style for contours if the global CONTOUR type
|
||||
// renderFill style property has not been changed.
|
||||
if (!contourRenderFillChangedGlobally) {
|
||||
segmentationService.setStyle(
|
||||
{ segmentationId, type: SegmentationRepresentations.Contour },
|
||||
{
|
||||
renderFill: true,
|
||||
renderFillInactive: true,
|
||||
},
|
||||
// Do not merge so that these created contours inherit other type-specific style properties like the fill alpha.
|
||||
// Merging would otherwise permanently inherit the fill alpha and any inheritance from the type level would be lost.
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
// If the global CONTOUR type renderFill style property is already set, do not subscribe to the SEGMENTATION_STYLE_MODIFIED event.
|
||||
if (contourRenderFillChangedGlobally) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Subscribe to the SEGMENTATION_STYLE_MODIFIED event to listen for changes to the CONTOUR type renderFill style property.
|
||||
const { unsubscribe } = segmentationService.subscribe(
|
||||
segmentationService.EVENTS.SEGMENTATION_STYLE_MODIFIED,
|
||||
({ specifier, style }) => {
|
||||
if (
|
||||
specifier.type === SegmentationRepresentations.Contour &&
|
||||
specifier.segmentationId == null &&
|
||||
specifier.viewportId == null &&
|
||||
style.renderFill != null
|
||||
) {
|
||||
// We are here because the renderFill style property is globally being changed for ALL contours.
|
||||
// When this occurs, the desire is for ALL contours to inherit the property. To make this happen,
|
||||
// we have to clear the style property that was set for this specific segmentation
|
||||
// when it was created above.
|
||||
// We can now also unsubscribe because this change only needs to be made when the global CONTOUR type
|
||||
// renderFill style property is first changed.
|
||||
|
||||
contourRenderFillChangedGlobally = true;
|
||||
unsubscribe();
|
||||
segmentationService.setStyle(
|
||||
{ segmentationId, type: SegmentationRepresentations.Contour },
|
||||
{},
|
||||
false
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
},
|
||||
'panelSegmentation.tableMode': 'collapsed',
|
||||
'panelSegmentation.readableText': {
|
||||
@@ -33,67 +109,11 @@ export default function getSegmentationPanelCustomization({ commandsManager, ser
|
||||
lesionGlycolysis: 'Lesion Glycolysis',
|
||||
center: 'Center',
|
||||
},
|
||||
'segmentationToolbox.config': () => {
|
||||
// Get initial states based on current configuration
|
||||
const [previewEdits, setPreviewEdits] = useState(false);
|
||||
const [segmentLabelEnabled, setSegmentLabelEnabled] = useState(false);
|
||||
const [toggleSegmentEnabled, setToggleSegmentEnabled] = useState(false);
|
||||
const [useCenterAsSegmentIndex, setUseCenterAsSegmentIndex] = useState(false);
|
||||
const handlePreviewEditsChange = checked => {
|
||||
setPreviewEdits(checked);
|
||||
commandsManager.run('toggleSegmentPreviewEdit', { toggle: checked });
|
||||
};
|
||||
|
||||
const handleToggleSegmentEnabledChange = checked => {
|
||||
setToggleSegmentEnabled(checked);
|
||||
commandsManager.run('toggleSegmentSelect', { toggle: checked });
|
||||
};
|
||||
|
||||
const handleUseCenterAsSegmentIndexChange = checked => {
|
||||
setUseCenterAsSegmentIndex(checked);
|
||||
commandsManager.run('toggleUseCenterSegmentIndex', { toggle: checked });
|
||||
};
|
||||
|
||||
const handleSegmentLabelEnabledChange = checked => {
|
||||
setSegmentLabelEnabled(checked);
|
||||
commandsManager.run('toggleSegmentLabel', { enabled: checked });
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="bg-muted flex flex-col gap-2 border-b border-b-[2px] border-black px-2 py-3">
|
||||
<div className="flex items-center gap-2">
|
||||
<Switch
|
||||
checked={previewEdits}
|
||||
onCheckedChange={handlePreviewEditsChange}
|
||||
/>
|
||||
<span className="text-foreground text-base">Preview edits before creating</span>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-2">
|
||||
<Switch
|
||||
checked={useCenterAsSegmentIndex}
|
||||
onCheckedChange={handleUseCenterAsSegmentIndexChange}
|
||||
/>
|
||||
<span className="text-foreground text-base">Use center as segment index</span>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-2">
|
||||
<Switch
|
||||
checked={toggleSegmentEnabled}
|
||||
onCheckedChange={handleToggleSegmentEnabledChange}
|
||||
/>
|
||||
<span className="text-foreground text-base">Hover on segment border to activate</span>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-2">
|
||||
<Switch
|
||||
checked={segmentLabelEnabled}
|
||||
onCheckedChange={handleSegmentLabelEnabledChange}
|
||||
/>
|
||||
<span className="text-foreground text-base">Show segment name on hover</span>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
'labelMapSegmentationToolbox.config': () => {
|
||||
return <SegmentationToolConfig />;
|
||||
},
|
||||
'contourSegmentationToolbox.config': () => {
|
||||
return <SegmentationToolConfig />;
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -1,53 +1,67 @@
|
||||
import React from 'react';
|
||||
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Toolbox } from '@ohif/extension-default';
|
||||
import PanelSegmentation from './panels/PanelSegmentation';
|
||||
import ActiveViewportWindowLevel from './components/ActiveViewportWindowLevel';
|
||||
import PanelMeasurement from './panels/PanelMeasurement';
|
||||
import { SegmentationRepresentations } from '@cornerstonejs/tools/enums';
|
||||
import i18n from '@ohif/i18n';
|
||||
|
||||
const getPanelModule = ({ commandsManager, servicesManager, extensionManager }: withAppTypes) => {
|
||||
const wrappedPanelSegmentation = ({ configuration }) => {
|
||||
const { toolbarService } = servicesManager.services;
|
||||
|
||||
const toolSectionMap = {
|
||||
[SegmentationRepresentations.Labelmap]: toolbarService.sections.labelMapSegmentationToolbox,
|
||||
[SegmentationRepresentations.Contour]: toolbarService.sections.contourSegmentationToolbox,
|
||||
};
|
||||
|
||||
const wrappedPanelSegmentation = props => {
|
||||
return (
|
||||
<PanelSegmentation
|
||||
commandsManager={commandsManager}
|
||||
servicesManager={servicesManager}
|
||||
extensionManager={extensionManager}
|
||||
configuration={{
|
||||
...configuration,
|
||||
...props?.configuration,
|
||||
}}
|
||||
segmentationRepresentationType={props?.segmentationRepresentationType}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const wrappedPanelSegmentationNoHeader = ({ configuration }) => {
|
||||
const wrappedPanelSegmentationNoHeader = props => {
|
||||
return (
|
||||
<PanelSegmentation
|
||||
commandsManager={commandsManager}
|
||||
servicesManager={servicesManager}
|
||||
extensionManager={extensionManager}
|
||||
configuration={{
|
||||
...configuration,
|
||||
...props?.configuration,
|
||||
}}
|
||||
segmentationRepresentationType={props?.segmentationRepresentationType}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const wrappedPanelSegmentationWithTools = ({ configuration }) => {
|
||||
const { toolbarService } = servicesManager.services;
|
||||
const wrappedPanelSegmentationWithTools = props => {
|
||||
const { t } = useTranslation('SegmentationTable');
|
||||
const tKey = `${props.segmentationRepresentationType ?? 'Segmentation'} tools`;
|
||||
const tValue = t(tKey);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Toolbox
|
||||
buttonSectionId={toolbarService.sections.segmentationToolbox}
|
||||
title="Segmentation Tools"
|
||||
buttonSectionId={toolSectionMap[props.segmentationRepresentationType]}
|
||||
title={tValue}
|
||||
/>
|
||||
<PanelSegmentation
|
||||
commandsManager={commandsManager}
|
||||
servicesManager={servicesManager}
|
||||
extensionManager={extensionManager}
|
||||
configuration={{
|
||||
...configuration,
|
||||
...props?.configuration,
|
||||
}}
|
||||
segmentationRepresentationType={props?.segmentationRepresentationType}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
@@ -82,11 +96,26 @@ const getPanelModule = ({ commandsManager, servicesManager, extensionManager }:
|
||||
component: wrappedPanelSegmentationNoHeader,
|
||||
},
|
||||
{
|
||||
name: 'panelSegmentationWithTools',
|
||||
name: 'panelSegmentationWithToolsLabelMap',
|
||||
iconName: 'tab-segmentation',
|
||||
iconLabel: 'Segmentation',
|
||||
label: 'Segmentation',
|
||||
component: wrappedPanelSegmentationWithTools,
|
||||
label: i18n.t('SegmentationTable:Labelmap'),
|
||||
component: props =>
|
||||
wrappedPanelSegmentationWithTools({
|
||||
...props,
|
||||
segmentationRepresentationType: SegmentationRepresentations.Labelmap,
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: 'panelSegmentationWithToolsContour',
|
||||
iconName: 'tab-contours',
|
||||
iconLabel: 'Segmentation',
|
||||
label: i18n.t('SegmentationTable:Contour'),
|
||||
component: props =>
|
||||
wrappedPanelSegmentationWithTools({
|
||||
...props,
|
||||
segmentationRepresentationType: SegmentationRepresentations.Contour,
|
||||
}),
|
||||
},
|
||||
];
|
||||
};
|
||||
|
||||
@@ -682,7 +682,7 @@ export function useViewportRendering(
|
||||
|
||||
const actorEntries = viewport.getActors();
|
||||
const actorEntry = actorEntries?.find(entry =>
|
||||
entry.referencedId.includes(activeDisplaySetInstanceUID)
|
||||
entry.referencedId?.includes(activeDisplaySetInstanceUID)
|
||||
);
|
||||
|
||||
if (!actorEntry) {
|
||||
|
||||
@@ -44,6 +44,7 @@ import {
|
||||
usePositionPresentationStore,
|
||||
useSegmentationPresentationStore,
|
||||
useSynchronizersStore,
|
||||
useSelectedSegmentationsForViewportStore,
|
||||
} from './stores';
|
||||
import { useToggleOneUpViewportGridStore } from '@ohif/extension-default';
|
||||
import { useActiveViewportSegmentationRepresentations } from './hooks/useActiveViewportSegmentationRepresentations';
|
||||
@@ -57,6 +58,7 @@ import CornerstoneViewportDownloadForm from './utils/CornerstoneViewportDownload
|
||||
import utils from './utils';
|
||||
import { useMeasurementTracking } from './hooks/useMeasurementTracking';
|
||||
import { setUpSegmentationEventHandlers } from './utils/setUpSegmentationEventHandlers';
|
||||
import { setUpAnnotationEventHandlers } from './utils/setUpAnnotationEventHandlers';
|
||||
export * from './components';
|
||||
|
||||
const { imageRetrieveMetadataProvider } = cornerstone.utilities;
|
||||
@@ -102,6 +104,9 @@ const cornerstoneExtension: Types.Extensions.Extension = {
|
||||
});
|
||||
unsubscriptions.push(...segmentationUnsubscriptions);
|
||||
|
||||
const annotationUnsubscriptions = setUpAnnotationEventHandlers();
|
||||
unsubscriptions.push(...annotationUnsubscriptions);
|
||||
|
||||
toolbarService.registerEventForToolbarUpdate(cornerstoneViewportService, [
|
||||
cornerstoneViewportService.EVENTS.VIEWPORT_DATA_CHANGED,
|
||||
]);
|
||||
@@ -109,6 +114,7 @@ const cornerstoneExtension: Types.Extensions.Extension = {
|
||||
toolbarService.registerEventForToolbarUpdate(segmentationService, [
|
||||
segmentationService.EVENTS.SEGMENTATION_REMOVED,
|
||||
segmentationService.EVENTS.SEGMENTATION_MODIFIED,
|
||||
segmentationService.EVENTS.SEGMENTATION_ANNOTATION_CUT_MERGE_PROCESS_COMPLETED,
|
||||
]);
|
||||
|
||||
toolbarService.registerEventForToolbarUpdate(cornerstone.eventTarget, [
|
||||
@@ -153,6 +159,9 @@ const cornerstoneExtension: Types.Extensions.Extension = {
|
||||
useSynchronizersStore.getState().clearSynchronizersStore();
|
||||
useToggleOneUpViewportGridStore.getState().clearToggleOneUpViewportGridStore();
|
||||
useSegmentationPresentationStore.getState().clearSegmentationPresentationStore();
|
||||
useSelectedSegmentationsForViewportStore
|
||||
.getState()
|
||||
.clearSelectedSegmentationsForViewportStore();
|
||||
segmentationService.removeAllSegmentations();
|
||||
},
|
||||
|
||||
@@ -256,6 +265,7 @@ export {
|
||||
usePositionPresentationStore,
|
||||
useSegmentationPresentationStore,
|
||||
useSynchronizersStore,
|
||||
useSelectedSegmentationsForViewportStore,
|
||||
Enums,
|
||||
useMeasurements,
|
||||
useActiveViewportSegmentationRepresentations,
|
||||
|
||||
@@ -106,6 +106,12 @@ export default async function init({
|
||||
colorbarService.EVENTS.STATE_CHANGED,
|
||||
]);
|
||||
|
||||
toolbarService.registerEventForToolbarUpdate(segmentationService, [
|
||||
segmentationService.EVENTS.SEGMENTATION_MODIFIED,
|
||||
segmentationService.EVENTS.SEGMENTATION_REPRESENTATION_MODIFIED,
|
||||
segmentationService.EVENTS.SEGMENTATION_ANNOTATION_CUT_MERGE_PROCESS_COMPLETED,
|
||||
]);
|
||||
|
||||
window.services = servicesManager.services;
|
||||
window.extensionManager = extensionManager;
|
||||
window.commandsManager = commandsManager;
|
||||
@@ -131,10 +137,18 @@ export default async function init({
|
||||
getSegmentationPresentationId
|
||||
);
|
||||
|
||||
cornerstoneTools.segmentation.config.style.setStyle(
|
||||
segmentationService.setStyle(
|
||||
{ type: SegmentationRepresentations.Contour },
|
||||
{
|
||||
// Declare these alpha values at the Contour type level so that they can be set/changed/inherited for all contour segmentations.
|
||||
fillAlpha: 0.5,
|
||||
fillAlphaInactive: 0.4,
|
||||
|
||||
// In general do not fill contours so that hydrated RTSTRUCTs are not filled in when active or inactive by default.
|
||||
// However, hydrated RTSTRUCTs are filled in when active or inactive if the user chooses to fill ALL contours.
|
||||
// Those Contours created in OHIF (i.e. using the Segmentation Panel) will override both fill properties upon creation.
|
||||
renderFill: false,
|
||||
renderFillInactive: false,
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
@@ -41,6 +41,10 @@ import {
|
||||
SegmentSelectTool,
|
||||
RegionSegmentPlusTool,
|
||||
SegmentLabelTool,
|
||||
LivewireContourSegmentationTool,
|
||||
SculptorTool,
|
||||
SplineContourSegmentationTool,
|
||||
LabelMapEditWithContourTool,
|
||||
} from '@cornerstonejs/tools';
|
||||
import { LabelmapSlicePropagationTool, MarkerLabelmapTool } from '@cornerstonejs/ai';
|
||||
import * as polySeg from '@cornerstonejs/polymorphic-segmentation';
|
||||
@@ -109,6 +113,10 @@ export default function initCornerstoneTools(configuration = {}) {
|
||||
addTool(LabelmapSlicePropagationTool);
|
||||
addTool(MarkerLabelmapTool);
|
||||
addTool(RegionSegmentPlusTool);
|
||||
addTool(LivewireContourSegmentationTool);
|
||||
addTool(SculptorTool);
|
||||
addTool(SplineContourSegmentationTool);
|
||||
addTool(LabelMapEditWithContourTool);
|
||||
// Modify annotation tools to use dashed lines on SR
|
||||
const annotationStyle = {
|
||||
textBoxFontSize: '15px',
|
||||
@@ -168,6 +176,10 @@ const toolNames = {
|
||||
LabelmapSlicePropagation: LabelmapSlicePropagationTool.toolName,
|
||||
MarkerLabelmap: MarkerLabelmapTool.toolName,
|
||||
RegionSegmentPlus: RegionSegmentPlusTool.toolName,
|
||||
LivewireContourSegmentation: LivewireContourSegmentationTool.toolName,
|
||||
SculptorTool: SculptorTool.toolName,
|
||||
SplineContourSegmentation: SplineContourSegmentationTool.toolName,
|
||||
LabelMapEditWithContourTool: LabelMapEditWithContourTool.toolName,
|
||||
};
|
||||
|
||||
export { toolNames };
|
||||
@@ -461,6 +461,9 @@ const connectMeasurementServiceToTools = ({
|
||||
const annotationManager = annotation.state.getAnnotationManager();
|
||||
const newAnnotation = {
|
||||
annotationUID: measurement.uid,
|
||||
// Not used in CS3D but stored in the CS3D state so that saving
|
||||
// can apply the predecessor consistently.
|
||||
predecessorImageId: measurement?.predecessorImageId,
|
||||
highlighted: false,
|
||||
isLocked: false,
|
||||
// This is used to force a re-render of the annotation to
|
||||
@@ -503,11 +506,15 @@ const connectMeasurementServiceToTools = ({
|
||||
}
|
||||
const removedAnnotation = annotation.state.getAnnotation(removedMeasurementId);
|
||||
removeAnnotation(removedMeasurementId);
|
||||
commandsManager.run('triggerCreateAnnotationMemo', {
|
||||
annotation: removedAnnotation,
|
||||
FrameOfReferenceUID: removedAnnotation.metadata.FrameOfReferenceUID,
|
||||
options: { deleting: true },
|
||||
});
|
||||
// Ensure `removedAnnotation` is available before triggering the memo,
|
||||
// as it can be undefined during an undo operation
|
||||
if (removedAnnotation) {
|
||||
commandsManager.run('triggerCreateAnnotationMemo', {
|
||||
annotation: removedAnnotation,
|
||||
FrameOfReferenceUID: removedAnnotation.metadata.FrameOfReferenceUID,
|
||||
options: { deleting: true },
|
||||
});
|
||||
}
|
||||
const renderingEngine = cornerstoneViewportService.getRenderingEngine();
|
||||
// Note: We could do a better job by triggering the render on the
|
||||
// viewport itself, but the removeAnnotation does not include that info...
|
||||
|
||||
@@ -1,16 +1,92 @@
|
||||
import React from 'react';
|
||||
import { SegmentationTable } from '@ohif/ui-next';
|
||||
import React, { useCallback, useEffect } from 'react';
|
||||
import {
|
||||
IconPresentationProvider,
|
||||
Popover,
|
||||
PopoverAnchor,
|
||||
PopoverContent,
|
||||
SegmentationTable,
|
||||
ToolSettings,
|
||||
} from '@ohif/ui-next';
|
||||
import { useActiveViewportSegmentationRepresentations } from '../hooks/useActiveViewportSegmentationRepresentations';
|
||||
import { metaData, cache } from '@cornerstonejs/core';
|
||||
import { useSystem } from '@ohif/core/src';
|
||||
import { useActiveToolOptions, useSystem } from '@ohif/core/src';
|
||||
import { SegmentationRepresentations } from '@cornerstonejs/tools/enums';
|
||||
import { Toolbar, useUIStateStore } from '@ohif/extension-default';
|
||||
import SegmentationUtilityButton from '../components/SegmentationUtilityButton';
|
||||
import { useSelectedSegmentationsForViewportStore } from '../stores';
|
||||
import {
|
||||
hasExportableLabelMapData,
|
||||
hasExportableContourData,
|
||||
} from '../utils/segmentationExportUtils';
|
||||
|
||||
export default function PanelSegmentation({ children }: withAppTypes) {
|
||||
type PanelSegmentationProps = {
|
||||
children?: React.ReactNode;
|
||||
|
||||
// The representation type for this segmentation panel. Undefined means all types.
|
||||
segmentationRepresentationType?: SegmentationRepresentations;
|
||||
} & withAppTypes;
|
||||
|
||||
export default function PanelSegmentation({
|
||||
children,
|
||||
segmentationRepresentationType,
|
||||
}: PanelSegmentationProps) {
|
||||
const { commandsManager, servicesManager } = useSystem();
|
||||
const { customizationService, displaySetService } = servicesManager.services;
|
||||
const {
|
||||
customizationService,
|
||||
displaySetService,
|
||||
viewportGridService,
|
||||
toolbarService,
|
||||
segmentationService,
|
||||
} = servicesManager.services;
|
||||
const { activeViewportId } = viewportGridService.getState();
|
||||
|
||||
const utilitiesSectionMap = {
|
||||
[SegmentationRepresentations.Labelmap]: toolbarService.sections.labelMapSegmentationUtilities,
|
||||
[SegmentationRepresentations.Contour]: toolbarService.sections.contourSegmentationUtilities,
|
||||
};
|
||||
|
||||
const selectedSegmentationsForViewportMap = useSelectedSegmentationsForViewportStore(
|
||||
store => store.selectedSegmentationsForViewport[activeViewportId]
|
||||
);
|
||||
|
||||
const selectedSegmentationIdForType = segmentationRepresentationType
|
||||
? selectedSegmentationsForViewportMap?.get(segmentationRepresentationType)
|
||||
: segmentationService?.getActiveSegmentation(activeViewportId)?.segmentationId;
|
||||
|
||||
const buttonSection = utilitiesSectionMap[segmentationRepresentationType];
|
||||
|
||||
const { activeToolOptions: activeUtilityOptions } = useActiveToolOptions({
|
||||
buttonSectionId: buttonSection,
|
||||
});
|
||||
|
||||
const { segmentationsWithRepresentations, disabled } =
|
||||
useActiveViewportSegmentationRepresentations();
|
||||
|
||||
const setUIState = useUIStateStore(store => store.setUIState);
|
||||
|
||||
// useEffect for handling clicks on any of the non-active viewports.
|
||||
// The ViewportGrid stops the propagation of pointer/mouse events
|
||||
// for non-active viewports so the Popover below
|
||||
// is not closed when clicking on any of the non-active viewports.
|
||||
useEffect(() => {
|
||||
setUIState('activeSegmentationUtility', null);
|
||||
toolbarService.refreshToolbarState({ viewportId: activeViewportId });
|
||||
}, [activeViewportId, setUIState, toolbarService]);
|
||||
|
||||
// The callback for handling clicks outside of the Popover and, the SegmentationUtilityButton
|
||||
// that triggered it to open. Clicks outside those components must close the Popover.
|
||||
// The Popover is made visible whenever the options associated with the
|
||||
// activeSegmentationUtility exist. Thus clearing the activeSegmentationUtility
|
||||
// clears the associated options and will keep the Popover closed.
|
||||
const handlePopoverOpenChange = useCallback(
|
||||
(open: boolean) => {
|
||||
if (!open) {
|
||||
setUIState('activeSegmentationUtility', null);
|
||||
toolbarService.refreshToolbarState({ viewportId: activeViewportId });
|
||||
}
|
||||
},
|
||||
[activeViewportId, setUIState, toolbarService]
|
||||
);
|
||||
|
||||
// Extract customization options
|
||||
const segmentationTableMode = customizationService.getCustomization(
|
||||
'panelSegmentation.tableMode'
|
||||
@@ -35,6 +111,7 @@ export default function PanelSegmentation({ children }: withAppTypes) {
|
||||
},
|
||||
onSegmentAdd: segmentationId => {
|
||||
commandsManager.run('addSegment', { segmentationId });
|
||||
commandsManager.run('setActiveSegmentation', { segmentationId });
|
||||
},
|
||||
onSegmentClick: (segmentationId, segmentIndex) => {
|
||||
commandsManager.run('setActiveSegmentAndCenter', { segmentationId, segmentIndex });
|
||||
@@ -51,6 +128,14 @@ export default function PanelSegmentation({ children }: withAppTypes) {
|
||||
onSegmentDelete: (segmentationId, segmentIndex) => {
|
||||
commandsManager.run('deleteSegment', { segmentationId, segmentIndex });
|
||||
},
|
||||
onSegmentCopy:
|
||||
segmentationRepresentationType === SegmentationRepresentations.Contour
|
||||
? (segmentationId, segmentIndex) => {
|
||||
commandsManager.run('copyContourSegment', {
|
||||
sourceSegmentInfo: { segmentationId, segmentIndex },
|
||||
});
|
||||
}
|
||||
: undefined,
|
||||
onToggleSegmentVisibility: (segmentationId, segmentIndex, type) => {
|
||||
commandsManager.run('toggleSegmentVisibility', { segmentationId, segmentIndex, type });
|
||||
},
|
||||
@@ -76,18 +161,36 @@ export default function PanelSegmentation({ children }: withAppTypes) {
|
||||
commandsManager.run('deleteSegmentation', { segmentationId });
|
||||
},
|
||||
setFillAlpha: ({ type }, value) => {
|
||||
commandsManager.run('activateSelectedSegmentationOfType', {
|
||||
segmentationRepresentationType: type,
|
||||
});
|
||||
commandsManager.run('setFillAlpha', { type, value });
|
||||
},
|
||||
setOutlineWidth: ({ type }, value) => {
|
||||
commandsManager.run('activateSelectedSegmentationOfType', {
|
||||
segmentationRepresentationType: type,
|
||||
});
|
||||
commandsManager.run('setOutlineWidth', { type, value });
|
||||
},
|
||||
setRenderFill: ({ type }, value) => {
|
||||
commandsManager.run('activateSelectedSegmentationOfType', {
|
||||
segmentationRepresentationType: type,
|
||||
});
|
||||
commandsManager.run('setRenderFill', { type, value });
|
||||
},
|
||||
setRenderFillInactive: ({ type }, value) => {
|
||||
commandsManager.run('setRenderFillInactive', { type, value });
|
||||
},
|
||||
setRenderOutline: ({ type }, value) => {
|
||||
commandsManager.run('activateSelectedSegmentationOfType', {
|
||||
segmentationRepresentationType: type,
|
||||
});
|
||||
commandsManager.run('setRenderOutline', { type, value });
|
||||
},
|
||||
setFillAlphaInactive: ({ type }, value) => {
|
||||
setRenderOutlineInactive: ({ type }, value) => {
|
||||
commandsManager.run('setRenderOutlineInactive', { type, value });
|
||||
},
|
||||
setFillAlphaInactive: ({ type }: { type?: string }, value) => {
|
||||
commandsManager.run('setFillAlphaInactive', { type, value });
|
||||
},
|
||||
getRenderInactiveSegmentations: () => {
|
||||
@@ -96,52 +199,26 @@ export default function PanelSegmentation({ children }: withAppTypes) {
|
||||
};
|
||||
|
||||
// Generate export options
|
||||
// Map each segmentation to an export option for it.
|
||||
// A segmentation is exportable if it has any labelmap or contour data.
|
||||
const exportOptions = segmentationsWithRepresentations.map(({ segmentation }) => {
|
||||
const { representationData, segmentationId } = segmentation;
|
||||
const { Labelmap } = representationData;
|
||||
const { Labelmap, Contour } = representationData;
|
||||
|
||||
if (!Labelmap) {
|
||||
if (!Labelmap && !Contour) {
|
||||
return { segmentationId, isExportable: true };
|
||||
}
|
||||
|
||||
// Check if any segments have anything drawn in any of the viewports
|
||||
const hasAnySegmentData = (() => {
|
||||
const imageIds = Labelmap.imageIds;
|
||||
if (!imageIds?.length) return false;
|
||||
|
||||
for (const imageId of imageIds) {
|
||||
const pixelData = cache.getImage(imageId)?.getPixelData();
|
||||
if (!pixelData) continue;
|
||||
|
||||
for (let i = 0; i < pixelData.length; i++) {
|
||||
if (pixelData[i] !== 0) return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
})();
|
||||
|
||||
if (!hasAnySegmentData) {
|
||||
if (
|
||||
!hasExportableLabelMapData(Labelmap, displaySetService) &&
|
||||
!hasExportableContourData(Contour)
|
||||
) {
|
||||
return { segmentationId, isExportable: false };
|
||||
}
|
||||
|
||||
const referencedImageIds = Labelmap.referencedImageIds;
|
||||
const firstImageId = referencedImageIds[0];
|
||||
const instance = metaData.get('instance', firstImageId);
|
||||
|
||||
if (!instance) {
|
||||
return { segmentationId, isExportable: false };
|
||||
}
|
||||
|
||||
const SOPInstanceUID = instance.SOPInstanceUID || instance.SopInstanceUID;
|
||||
const SeriesInstanceUID = instance.SeriesInstanceUID;
|
||||
const displaySet = displaySetService.getDisplaySetForSOPInstanceUID(
|
||||
SOPInstanceUID,
|
||||
SeriesInstanceUID
|
||||
);
|
||||
|
||||
return {
|
||||
segmentationId,
|
||||
isExportable: displaySet?.isReconstructable,
|
||||
isExportable: true,
|
||||
};
|
||||
});
|
||||
|
||||
@@ -150,15 +227,34 @@ export default function PanelSegmentation({ children }: withAppTypes) {
|
||||
disabled,
|
||||
data: segmentationsWithRepresentations,
|
||||
mode: segmentationTableMode,
|
||||
title: 'Segmentations',
|
||||
title: `${segmentationRepresentationType ? `${segmentationRepresentationType} ` : ''}Segmentations`,
|
||||
exportOptions,
|
||||
disableEditing,
|
||||
onSegmentationAdd,
|
||||
showAddSegment,
|
||||
renderInactiveSegmentations: handlers.getRenderInactiveSegmentations(),
|
||||
segmentationRepresentationType,
|
||||
selectedSegmentationIdForType,
|
||||
...handlers,
|
||||
};
|
||||
|
||||
const renderUtilitiesToolbar = () => {
|
||||
if (!buttonSection) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<IconPresentationProvider
|
||||
size="large"
|
||||
IconContainer={SegmentationUtilityButton}
|
||||
>
|
||||
<div className="flex flex-wrap gap-[3px] bg-transparent pb-[2px] pl-[8px] pt-[6px]">
|
||||
<Toolbar buttonSection={buttonSection} />
|
||||
</div>
|
||||
</IconPresentationProvider>
|
||||
);
|
||||
};
|
||||
|
||||
const renderSegments = () => {
|
||||
return (
|
||||
<SegmentationTable.Segments>
|
||||
@@ -175,6 +271,7 @@ export default function PanelSegmentation({ children }: withAppTypes) {
|
||||
if (tableProps.mode === 'collapsed') {
|
||||
return (
|
||||
<SegmentationTable.Collapsed>
|
||||
{renderUtilitiesToolbar()}
|
||||
<SegmentationTable.Collapsed.Header>
|
||||
<SegmentationTable.Collapsed.DropdownMenu>
|
||||
<CustomDropdownMenuContent />
|
||||
@@ -193,6 +290,7 @@ export default function PanelSegmentation({ children }: withAppTypes) {
|
||||
return (
|
||||
<>
|
||||
<SegmentationTable.Expanded>
|
||||
{renderUtilitiesToolbar()}
|
||||
<SegmentationTable.Expanded.Header>
|
||||
<SegmentationTable.Expanded.DropdownMenu>
|
||||
<CustomDropdownMenuContent />
|
||||
@@ -211,11 +309,27 @@ export default function PanelSegmentation({ children }: withAppTypes) {
|
||||
};
|
||||
|
||||
return (
|
||||
<SegmentationTable {...tableProps}>
|
||||
{children}
|
||||
<SegmentationTable.Config />
|
||||
<SegmentationTable.AddSegmentationRow />
|
||||
{renderModeContent()}
|
||||
</SegmentationTable>
|
||||
<Popover
|
||||
open={!!activeUtilityOptions}
|
||||
onOpenChange={handlePopoverOpenChange}
|
||||
>
|
||||
<PopoverAnchor>
|
||||
<SegmentationTable {...tableProps}>
|
||||
{children}
|
||||
<SegmentationTable.Config />
|
||||
<SegmentationTable.AddSegmentationRow />
|
||||
{renderModeContent()}
|
||||
</SegmentationTable>
|
||||
</PopoverAnchor>
|
||||
{activeUtilityOptions && (
|
||||
<PopoverContent
|
||||
side="left"
|
||||
align="start"
|
||||
className="w-auto"
|
||||
>
|
||||
<ToolSettings options={activeUtilityOptions} />
|
||||
</PopoverContent>
|
||||
)}
|
||||
</Popover>
|
||||
);
|
||||
}
|
||||
+2
-2
@@ -233,7 +233,7 @@ class CornerstoneCacheService {
|
||||
for (const displaySet of displaySets) {
|
||||
const { Modality } = displaySet;
|
||||
const isParametricMap = Modality === 'PMAP';
|
||||
const isSeg = Modality === 'SEG';
|
||||
const isSegOrRtstruct = Modality === 'SEG' || Modality === 'RTSTRUCT';
|
||||
|
||||
// Don't create volumes for the displaySets that have custom load
|
||||
// function (e.g., SEG, RT, since they rely on the reference volumes
|
||||
@@ -276,7 +276,7 @@ class CornerstoneCacheService {
|
||||
|
||||
// Parametric maps do not have image ids but they already have volume data
|
||||
// therefore a new volume should not be created.
|
||||
if (!isParametricMap && !isSeg && (!volumeImageIds || !volume)) {
|
||||
if (!isParametricMap && !isSegOrRtstruct && (!volumeImageIds || !volume)) {
|
||||
volumeImageIds = this._getCornerstoneVolumeImageIds(displaySet, dataSource);
|
||||
|
||||
volume = await volumeLoader.createAndCacheVolume(volumeId, {
|
||||
|
||||
+10
-4
@@ -221,7 +221,7 @@ describe('SegmentationService', () => {
|
||||
it('should add event listeners', () => {
|
||||
service.onModeEnter();
|
||||
|
||||
expect(eventTarget.addEventListener).toHaveBeenCalledTimes(7);
|
||||
expect(eventTarget.addEventListener).toHaveBeenCalledTimes(8);
|
||||
|
||||
expect(eventTarget.addEventListener).toHaveBeenCalledWith(
|
||||
csToolsEnums.Events.SEGMENTATION_MODIFIED,
|
||||
@@ -251,6 +251,10 @@ describe('SegmentationService', () => {
|
||||
csToolsEnums.Events.SEGMENTATION_ADDED,
|
||||
expect.any(Function)
|
||||
);
|
||||
expect(eventTarget.addEventListener).toHaveBeenCalledWith(
|
||||
csToolsEnums.Events.ANNOTATION_CUT_MERGE_PROCESS_COMPLETED,
|
||||
expect.any(Function)
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1950,7 +1954,8 @@ describe('SegmentationService', () => {
|
||||
type,
|
||||
segmentIndex,
|
||||
},
|
||||
style
|
||||
style,
|
||||
true
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -1999,7 +2004,7 @@ describe('SegmentationService', () => {
|
||||
|
||||
service.addSegment(segmentationId, config);
|
||||
|
||||
expect(cstSegmentation.state.getSegmentation).toHaveBeenCalledTimes(1);
|
||||
expect(cstSegmentation.state.getSegmentation).toHaveBeenCalledTimes(2);
|
||||
expect(cstSegmentation.state.getSegmentation).toHaveBeenCalledWith(segmentationId);
|
||||
|
||||
expect(cstSegmentation.updateSegmentations).toHaveBeenCalledTimes(1);
|
||||
@@ -2825,7 +2830,8 @@ describe('SegmentationService', () => {
|
||||
segmentIndex,
|
||||
type: csToolsEnums.SegmentationRepresentations.Labelmap,
|
||||
},
|
||||
{}
|
||||
{},
|
||||
false
|
||||
);
|
||||
|
||||
expect(window.requestAnimationFrame).not.toHaveBeenCalledTimes(3);
|
||||
|
||||
@@ -81,6 +81,10 @@ const EVENTS = {
|
||||
SEGMENT_LOADING_COMPLETE: 'event::segment_loading_complete',
|
||||
// loading completed for all segments
|
||||
SEGMENTATION_LOADING_COMPLETE: 'event::segmentation_loading_complete',
|
||||
// fired when a contour annotation cut merge process is completed
|
||||
SEGMENTATION_ANNOTATION_CUT_MERGE_PROCESS_COMPLETED:
|
||||
'event::annotation_cut_merge_process_completed',
|
||||
SEGMENTATION_STYLE_MODIFIED: 'event::segmentation_style_modified',
|
||||
};
|
||||
|
||||
const VALUE_TYPES = {};
|
||||
@@ -274,11 +278,13 @@ class SegmentationService extends PubSubService {
|
||||
viewportId: string,
|
||||
{
|
||||
segmentationId,
|
||||
predecessorImageId,
|
||||
type,
|
||||
config,
|
||||
suppressEvents = false,
|
||||
}: {
|
||||
segmentationId: string;
|
||||
predecessorImageId?: string;
|
||||
type?: csToolsEnums.SegmentationRepresentations;
|
||||
config?: {
|
||||
blendMode?: csEnums.BlendModes;
|
||||
@@ -287,6 +293,9 @@ class SegmentationService extends PubSubService {
|
||||
}
|
||||
): Promise<void> {
|
||||
const segmentation = this.getSegmentation(segmentationId);
|
||||
if (segmentation && !segmentation.predecessorImageId && predecessorImageId) {
|
||||
segmentation.predecessorImageId = predecessorImageId;
|
||||
}
|
||||
const csViewport = this.getAndValidateViewport(viewportId);
|
||||
|
||||
if (!csViewport) {
|
||||
@@ -350,6 +359,39 @@ class SegmentationService extends PubSubService {
|
||||
FrameOfReferenceUID?: string;
|
||||
label?: string;
|
||||
}
|
||||
): Promise<string> {
|
||||
return this._createSegmentationForDisplaySet(displaySet, LABELMAP, options);
|
||||
}
|
||||
|
||||
public async createContourForDisplaySet(
|
||||
displaySet: AppTypes.DisplaySet,
|
||||
options?: {
|
||||
segmentationId?: string;
|
||||
segments?: { [segmentIndex: number]: Partial<cstTypes.Segment> };
|
||||
FrameOfReferenceUID?: string;
|
||||
label?: string;
|
||||
}
|
||||
): Promise<string> {
|
||||
return this._createSegmentationForDisplaySet(displaySet, CONTOUR, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Private method to create segmentation for a display set with the specified type
|
||||
*
|
||||
* @param displaySet - The display set to create the segmentation for
|
||||
* @param segmentationType - The type of segmentation (SegmentationRepresentations enum)
|
||||
* @param options - Optional parameters for creating the segmentation
|
||||
* @returns A promise that resolves to the created segmentation ID
|
||||
*/
|
||||
private async _createSegmentationForDisplaySet(
|
||||
displaySet: AppTypes.DisplaySet,
|
||||
segmentationType: SegmentationRepresentations,
|
||||
options?: {
|
||||
segmentationId?: string;
|
||||
segments?: { [segmentIndex: number]: Partial<cstTypes.Segment> };
|
||||
FrameOfReferenceUID?: string;
|
||||
label?: string;
|
||||
}
|
||||
): Promise<string> {
|
||||
// Todo: random does not makes sense, make this better, like
|
||||
// labelmap 1, 2, 3 etc
|
||||
@@ -375,7 +417,7 @@ class SegmentationService extends PubSubService {
|
||||
const segmentationPublicInput: cstTypes.SegmentationPublicInput = {
|
||||
segmentationId,
|
||||
representation: {
|
||||
type: LABELMAP,
|
||||
type: segmentationType,
|
||||
data: {
|
||||
imageIds: segImageIds,
|
||||
// referencedVolumeId: this._getVolumeIdForDisplaySet(displaySet),
|
||||
@@ -799,15 +841,30 @@ class SegmentationService extends PubSubService {
|
||||
segmentationId?: string;
|
||||
segmentIndex?: number;
|
||||
},
|
||||
style: cstTypes.LabelmapStyle | cstTypes.ContourStyle | cstTypes.SurfaceStyle
|
||||
style: cstTypes.LabelmapStyle | cstTypes.ContourStyle | cstTypes.SurfaceStyle,
|
||||
merge: boolean = true
|
||||
) => {
|
||||
cstSegmentation.config.style.setStyle(specifier, style);
|
||||
cstSegmentation.config.style.setStyle(specifier, style, merge);
|
||||
this._broadcastEvent(EVENTS.SEGMENTATION_STYLE_MODIFIED, {
|
||||
specifier,
|
||||
style,
|
||||
merge,
|
||||
});
|
||||
};
|
||||
|
||||
public resetToGlobalStyle = () => {
|
||||
cstSegmentation.config.style.resetToGlobalStyle();
|
||||
};
|
||||
|
||||
public getNextAvailableSegmentIndex(segmentationId: string): number {
|
||||
const csSegmentation = this.getCornerstoneSegmentation(segmentationId);
|
||||
// grab the next available segment index based on the object keys,
|
||||
// so basically get the highest segment index value + 1
|
||||
|
||||
const segmentKeys = Object.keys(csSegmentation.segments);
|
||||
return segmentKeys.length === 0 ? 1 : Math.max(...segmentKeys.map(Number)) + 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new segment to the specified segmentation.
|
||||
* @param segmentationId - The ID of the segmentation to add the segment to.
|
||||
@@ -841,10 +898,7 @@ class SegmentationService extends PubSubService {
|
||||
|
||||
let segmentIndex = config.segmentIndex;
|
||||
if (!segmentIndex) {
|
||||
// grab the next available segment index based on the object keys,
|
||||
// so basically get the highest segment index value + 1
|
||||
const segmentKeys = Object.keys(csSegmentation.segments);
|
||||
segmentIndex = segmentKeys.length === 0 ? 1 : Math.max(...segmentKeys.map(Number)) + 1;
|
||||
segmentIndex = this.getNextAvailableSegmentIndex(segmentationId);
|
||||
}
|
||||
|
||||
// update the segmentation
|
||||
@@ -1627,6 +1681,11 @@ class SegmentationService extends PubSubService {
|
||||
csToolsEnums.Events.SEGMENTATION_ADDED,
|
||||
this._onSegmentationAddedFromSource
|
||||
);
|
||||
|
||||
eventTarget.addEventListener(
|
||||
csToolsEnums.Events.ANNOTATION_CUT_MERGE_PROCESS_COMPLETED,
|
||||
this._onAnnotationCutMergeProcessCompletedFromSource
|
||||
);
|
||||
}
|
||||
|
||||
private getCornerstoneSegmentation(segmentationId: string) {
|
||||
@@ -1650,13 +1709,6 @@ class SegmentationService extends PubSubService {
|
||||
|
||||
if (hideOthers) {
|
||||
throw new Error('hideOthers is not working right now');
|
||||
for (let i = 0; i < segments.length; i++) {
|
||||
if (i !== segmentIndex) {
|
||||
newSegmentSpecificConfig[i] = {
|
||||
fillAlpha: 0,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const { fillAlpha } = this.getStyle({
|
||||
@@ -1697,7 +1749,8 @@ class SegmentationService extends PubSubService {
|
||||
segmentIndex,
|
||||
type: LABELMAP,
|
||||
},
|
||||
{}
|
||||
{},
|
||||
false
|
||||
);
|
||||
}
|
||||
};
|
||||
@@ -1879,6 +1932,13 @@ class SegmentationService extends PubSubService {
|
||||
segmentationId,
|
||||
});
|
||||
};
|
||||
|
||||
private _onAnnotationCutMergeProcessCompletedFromSource = evt => {
|
||||
const { segmentationId } = evt.detail;
|
||||
this._broadcastEvent(this.EVENTS.SEGMENTATION_ANNOTATION_CUT_MERGE_PROCESS_COMPLETED, {
|
||||
segmentationId,
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
export default SegmentationService;
|
||||
|
||||
+7
-8
@@ -43,7 +43,7 @@ const segmentationRepresentationModifiedCallback = async (
|
||||
) => {
|
||||
const event = sourceEvent as ToolsTypes.EventTypes.SegmentationRepresentationModifiedEventType;
|
||||
|
||||
const { segmentationId } = event.detail;
|
||||
const { segmentationId, type: segmentationRepresentationType } = event.detail;
|
||||
const { segmentationService } = servicesManager.services;
|
||||
|
||||
const targetViewportId = targetViewport.viewportId;
|
||||
@@ -65,13 +65,12 @@ const segmentationRepresentationModifiedCallback = async (
|
||||
return;
|
||||
}
|
||||
|
||||
// whatever type the source viewport has, we need to add that to the target viewport
|
||||
const sourceViewportRepresentation = segmentationService.getSegmentationRepresentations(
|
||||
sourceViewport.viewportId,
|
||||
{ segmentationId }
|
||||
);
|
||||
|
||||
const type = sourceViewportRepresentation[0].type;
|
||||
// Ensure the segmentation representation aligns with the target viewport type.
|
||||
const type: Enums.SegmentationRepresentations =
|
||||
viewport.type === CoreEnums.ViewportType.VOLUME_3D
|
||||
? Enums.SegmentationRepresentations.Surface
|
||||
: ((segmentationRepresentationType as Enums.SegmentationRepresentations) ??
|
||||
Enums.SegmentationRepresentations.Labelmap);
|
||||
|
||||
await segmentationService.addSegmentationRepresentation(targetViewportId, {
|
||||
segmentationId,
|
||||
|
||||
@@ -714,7 +714,7 @@ class CornerstoneViewportService extends PubSubService implements IViewportServi
|
||||
// is being used to navigate to the initial view position for measurement
|
||||
// navigation and other navigation forcing specific views.
|
||||
let initialImageIndexToUse =
|
||||
presentations?.positionPresentation?.initialImageIndex ?? <number>initialImageIndex;
|
||||
presentations?.positionPresentation?.initialImageIndex ?? (initialImageIndex as number);
|
||||
|
||||
const { rotation, flipHorizontal, displayArea } = viewportInfo.getViewportOptions();
|
||||
|
||||
@@ -933,6 +933,10 @@ class CornerstoneViewportService extends PubSubService implements IViewportServi
|
||||
const displaySet = displaySetService.getDisplaySetByUID(displaySetUIDs[0]);
|
||||
const displaySetModality = displaySet?.Modality;
|
||||
|
||||
// seems like a hack but we need the actor to be ready first before
|
||||
// we set the properties
|
||||
const timeoutViewportCallback = (callback: () => void) => setTimeout(callback, 0);
|
||||
|
||||
// filter overlay display sets (e.g. segmentation) since they will get handled below via the segmentation service
|
||||
const filteredVolumeInputArray = volumeInputArray
|
||||
.map((volumeInput, index) => {
|
||||
@@ -984,12 +988,21 @@ class CornerstoneViewportService extends PubSubService implements IViewportServi
|
||||
const backgroundDisplaySet = displaySetService.getDisplaySetsBy(
|
||||
displaySet =>
|
||||
!displaySet.isOverlayDisplaySet &&
|
||||
displaySet.images.some(image => image.imageId === sampleImageId)
|
||||
displaySet.images?.some(image => image.imageId === sampleImageId)
|
||||
);
|
||||
|
||||
if (backgroundDisplaySet.length !== 1) {
|
||||
throw new Error('Background display set not found');
|
||||
}
|
||||
|
||||
if (viewport.type === csEnums.ViewportType.VOLUME_3D) {
|
||||
timeoutViewportCallback(() => {
|
||||
viewportGridService.setDisplaySetsForViewport({
|
||||
viewportId: viewport.id,
|
||||
displaySetInstanceUIDs: [backgroundDisplaySet[0].displaySetInstanceUID],
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -1006,15 +1019,13 @@ class CornerstoneViewportService extends PubSubService implements IViewportServi
|
||||
viewport.render();
|
||||
|
||||
volumesProperties.forEach(({ properties, volumeId }) => {
|
||||
setTimeout(() => {
|
||||
// seems like a hack but we need the actor to be ready first before
|
||||
// we set the properties
|
||||
timeoutViewportCallback(() => {
|
||||
viewport.setProperties(properties, volumeId);
|
||||
viewport.render();
|
||||
}, 0);
|
||||
});
|
||||
});
|
||||
|
||||
this.setPresentations(viewport.id, presentations, viewportInfo);
|
||||
this.setPresentations(viewport.id, presentations);
|
||||
|
||||
if (!presentations.positionPresentation) {
|
||||
const imageIndex = this._getInitialImageIndexForViewport(viewportInfo);
|
||||
@@ -1079,8 +1090,10 @@ class CornerstoneViewportService extends PubSubService implements IViewportServi
|
||||
? csToolsEnums.SegmentationRepresentations.Labelmap
|
||||
: csToolsEnums.SegmentationRepresentations.Contour;
|
||||
|
||||
const { predecessorImageId } = displaySet;
|
||||
segmentationService.addSegmentationRepresentation(viewport.id, {
|
||||
segmentationId,
|
||||
predecessorImageId,
|
||||
type: representationType,
|
||||
});
|
||||
|
||||
@@ -1336,17 +1349,12 @@ class CornerstoneViewportService extends PubSubService implements IViewportServi
|
||||
const { segmentationId, type, hydrated } = presentationItem;
|
||||
|
||||
const { Labelmap, Surface } = csToolsEnums.SegmentationRepresentations;
|
||||
const representationType = (() => {
|
||||
if (type === Surface) {
|
||||
if (viewport.type === csEnums.ViewportType.VOLUME_3D) {
|
||||
return Surface;
|
||||
} else {
|
||||
return Labelmap;
|
||||
}
|
||||
}
|
||||
const isVolume3D = viewport.type === csEnums.ViewportType.VOLUME_3D;
|
||||
|
||||
return type;
|
||||
})();
|
||||
// Determine the appropriate segmentation representation for the viewport.
|
||||
// If the current type is Surface but the viewport is not 3D, fallback to Labelmap.
|
||||
// Otherwise, use the existing type.
|
||||
const representationType = type === Surface && !isVolume3D ? Labelmap : type;
|
||||
|
||||
if (hydrated) {
|
||||
segmentationService.addSegmentationRepresentation(viewport.id, {
|
||||
|
||||
@@ -2,3 +2,4 @@ export { useLutPresentationStore } from './useLutPresentationStore';
|
||||
export { usePositionPresentationStore } from './usePositionPresentationStore';
|
||||
export { useSegmentationPresentationStore } from './useSegmentationPresentationStore';
|
||||
export { useSynchronizersStore } from './useSynchronizersStore';
|
||||
export { useSelectedSegmentationsForViewportStore } from './useSelectedSegmentationsForViewportStore';
|
||||
@@ -0,0 +1,85 @@
|
||||
import { create } from 'zustand';
|
||||
import { devtools } from 'zustand/middleware';
|
||||
import { SelectedSegmentationByTypeMap } from '../types/SelectedSegmentationByTypeMap';
|
||||
|
||||
const PRESENTATION_TYPE_ID = 'selectedSegmentationsForViewportId';
|
||||
const DEBUG_STORE = false;
|
||||
|
||||
/**
|
||||
* Represents the state and actions for managing selected segmentations for a viewport by representation type.
|
||||
*/
|
||||
type SelectedSegmentationsForViewportState = {
|
||||
/**
|
||||
* Stores a mapping from viewport id to a map of representation type to segmentation id.
|
||||
*/
|
||||
selectedSegmentationsForViewport: Record<string, SelectedSegmentationByTypeMap>;
|
||||
|
||||
/**
|
||||
* Sets the selected segmentations for a given viewport id.
|
||||
*
|
||||
* @param key - The viewport id.
|
||||
* @param value - The `SelectedSegmentationByTypeMap` to associate with the viewport id.
|
||||
*/
|
||||
setSelectedSegmentationsForViewport: (key: string, value: SelectedSegmentationByTypeMap) => void;
|
||||
|
||||
/**
|
||||
* Clears all selected segmentations for all viewports.
|
||||
*/
|
||||
clearSelectedSegmentationsForViewportStore: () => void;
|
||||
|
||||
/**
|
||||
* Type identifier for the store.
|
||||
*/
|
||||
type: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates the Selected Segmentations For Viewport store.
|
||||
*
|
||||
* @param set - The zustand set function.
|
||||
* @returns The selected segmentations for viewport store state and actions.
|
||||
*/
|
||||
const createSelectedSegmentationsForViewportStore = (
|
||||
set
|
||||
): SelectedSegmentationsForViewportState => ({
|
||||
selectedSegmentationsForViewport: {},
|
||||
type: PRESENTATION_TYPE_ID,
|
||||
|
||||
/**
|
||||
* Sets the selected segmentations for a given viewport id.
|
||||
*/
|
||||
setSelectedSegmentationsForViewport: (key, value) =>
|
||||
set(
|
||||
state => ({
|
||||
selectedSegmentationsForViewport: {
|
||||
...state.selectedSegmentationsForViewport,
|
||||
[key]: value,
|
||||
},
|
||||
}),
|
||||
false,
|
||||
'setSelectedSegmentationsForViewport'
|
||||
),
|
||||
|
||||
/**
|
||||
* Clears all selected segmentations for all viewports.
|
||||
*/
|
||||
clearSelectedSegmentationsForViewportStore: () =>
|
||||
set(
|
||||
{ selectedSegmentationsForViewport: {} },
|
||||
false,
|
||||
'clearSelectedSegmentationsForViewportStore'
|
||||
),
|
||||
});
|
||||
|
||||
/**
|
||||
* Zustand store for managing selected segmentations for a viewport by representation type.
|
||||
* Applies devtools middleware when DEBUG_STORE is enabled.
|
||||
*/
|
||||
export const useSelectedSegmentationsForViewportStore =
|
||||
create<SelectedSegmentationsForViewportState>()(
|
||||
DEBUG_STORE
|
||||
? devtools(createSelectedSegmentationsForViewportStore, {
|
||||
name: 'SelectedSegmentationsForViewportStore',
|
||||
})
|
||||
: createSelectedSegmentationsForViewportStore
|
||||
);
|
||||
@@ -0,0 +1,3 @@
|
||||
import { SegmentationRepresentations } from '@cornerstonejs/tools/enums';
|
||||
|
||||
export type SelectedSegmentationByTypeMap = Map<SegmentationRepresentations, string>;
|
||||
@@ -1,3 +1,4 @@
|
||||
import { utils } from '@ohif/core';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import html2canvas from 'html2canvas';
|
||||
import { getEnabledElement, StackViewport, BaseVolumeViewport } from '@cornerstonejs/core';
|
||||
@@ -5,6 +6,8 @@ import { ToolGroupManager, segmentation, Enums } from '@cornerstonejs/tools';
|
||||
import { getEnabledElement as OHIFgetEnabledElement } from '../state';
|
||||
import { useSystem } from '@ohif/core/src';
|
||||
|
||||
const { downloadUrl } = utils;
|
||||
|
||||
const DEFAULT_SIZE = 512;
|
||||
const MAX_TEXTURE_SIZE = 10000;
|
||||
const VIEWPORT_ID = 'cornerstone-viewport-download-form';
|
||||
@@ -218,10 +221,7 @@ const CornerstoneViewportDownloadForm = ({
|
||||
}
|
||||
|
||||
const canvas = await html2canvas(divForDownloadViewport as HTMLElement);
|
||||
const link = document.createElement('a');
|
||||
link.download = `${filename}.${fileType}`;
|
||||
link.href = canvas.toDataURL(`image/${fileType}`, 1.0);
|
||||
link.click();
|
||||
downloadUrl(canvas.toDataURL(`image/${fileType}`, 1.0));
|
||||
};
|
||||
|
||||
const ViewportDownloadFormNew = customizationService.getCustomization(
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
import { utilities as csUtils } from '@cornerstonejs/core';
|
||||
import { SegmentationRepresentations } from '@cornerstonejs/tools/enums';
|
||||
import i18n from '@ohif/i18n';
|
||||
import { ServicesManager } from '@ohif/core';
|
||||
|
||||
function _createDefaultSegments(createInitialSegment?: boolean) {
|
||||
return createInitialSegment
|
||||
? {
|
||||
1: {
|
||||
label: `${i18n.t('Tools:Segment')} 1`,
|
||||
active: true,
|
||||
},
|
||||
}
|
||||
: {};
|
||||
}
|
||||
|
||||
type CreateSegmentationForViewportOptions = {
|
||||
displaySetInstanceUID?: string;
|
||||
label?: string;
|
||||
segmentationId?: string;
|
||||
createInitialSegment?: boolean;
|
||||
};
|
||||
|
||||
type CreateSegmentationForViewportParams = {
|
||||
viewportId: string;
|
||||
options?: CreateSegmentationForViewportOptions;
|
||||
segmentationType: SegmentationRepresentations;
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a segmentation for the active viewport
|
||||
*
|
||||
* The created segmentation will be registered as a display set and also added
|
||||
* as a segmentation representation to the viewport.
|
||||
*/
|
||||
export async function createSegmentationForViewport(
|
||||
servicesManager: ServicesManager,
|
||||
{ viewportId, options = {}, segmentationType }: CreateSegmentationForViewportParams
|
||||
): Promise<string> {
|
||||
const { viewportGridService, displaySetService, segmentationService } = servicesManager.services;
|
||||
const { viewports } = viewportGridService.getState();
|
||||
const targetViewportId = viewportId;
|
||||
|
||||
const viewport = viewports.get(targetViewportId);
|
||||
|
||||
// Todo: add support for multiple display sets
|
||||
const displaySetInstanceUID = options.displaySetInstanceUID || viewport.displaySetInstanceUIDs[0];
|
||||
|
||||
const segs = segmentationService.getSegmentations();
|
||||
|
||||
const label = options.label || `${i18n.t('Tools:Segmentation')} ${segs.length + 1}`;
|
||||
const segmentationId = options.segmentationId || `${csUtils.uuidv4()}`;
|
||||
|
||||
const displaySet = displaySetService.getDisplaySetByUID(displaySetInstanceUID);
|
||||
|
||||
const segmentationCreationOptions = {
|
||||
label,
|
||||
segmentationId,
|
||||
segments: _createDefaultSegments(options.createInitialSegment),
|
||||
};
|
||||
|
||||
// This will create the segmentation and register it as a display set
|
||||
const generatedSegmentationId = await (segmentationType === SegmentationRepresentations.Labelmap
|
||||
? segmentationService.createLabelmapForDisplaySet(displaySet, segmentationCreationOptions)
|
||||
: segmentationService.createContourForDisplaySet(displaySet, segmentationCreationOptions));
|
||||
|
||||
// Also add the segmentation representation to the viewport
|
||||
await segmentationService.addSegmentationRepresentation(viewportId, {
|
||||
segmentationId,
|
||||
type: segmentationType,
|
||||
});
|
||||
|
||||
return generatedSegmentationId;
|
||||
}
|
||||
@@ -13,6 +13,7 @@ Object.defineProperty(global, 'URL', {
|
||||
writable: true,
|
||||
value: {
|
||||
createObjectURL: jest.fn().mockReturnValue('mock-url'),
|
||||
revokeObjectURL: () => undefined,
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
import { utils } from '@ohif/core';
|
||||
|
||||
const { downloadCsv } = utils;
|
||||
|
||||
export function generateSegmentationCSVReport(
|
||||
segmentationData,
|
||||
info: {
|
||||
@@ -121,19 +125,7 @@ export function generateSegmentationCSVReport(
|
||||
csvString += formattedRow.join(',') + '\n';
|
||||
}
|
||||
|
||||
// Create a download link and trigger the download
|
||||
const blob = new Blob([csvString], { type: 'text/csv;charset=utf-8;' });
|
||||
const url = URL.createObjectURL(blob);
|
||||
const link = document.createElement('a');
|
||||
|
||||
link.setAttribute('href', url);
|
||||
link.setAttribute(
|
||||
'download',
|
||||
`${segmentationData.label || 'Segmentation'}_Report_${new Date().toISOString().split('T')[0]}.csv`
|
||||
);
|
||||
link.style.visibility = 'hidden';
|
||||
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
downloadCsv(csvString, {
|
||||
filename: `${segmentationData.label || 'Segmentation'}_Report_${new Date().toISOString().split('T')[0]}.csv`,
|
||||
});
|
||||
}
|
||||
@@ -70,43 +70,6 @@ describe('getUpdatedViewportsForSegmentation', () => {
|
||||
expect(result).toEqual(mockUpdatedViewports);
|
||||
});
|
||||
|
||||
it('should filter out volume3d viewports', () => {
|
||||
const mockUpdatedViewports = [
|
||||
{
|
||||
viewportOptions: {
|
||||
viewportType: 'stack',
|
||||
},
|
||||
},
|
||||
{
|
||||
viewportOptions: {
|
||||
viewportType: 'volume3d',
|
||||
},
|
||||
},
|
||||
{
|
||||
viewportOptions: {
|
||||
viewportType: 'volume',
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
mockHangingProtocolService.getViewportsRequireUpdate.mockReturnValue(mockUpdatedViewports);
|
||||
|
||||
const result = getUpdatedViewportsForSegmentation(defaultParameters);
|
||||
|
||||
expect(result).toEqual([
|
||||
{
|
||||
viewportOptions: {
|
||||
viewportType: 'stack',
|
||||
},
|
||||
},
|
||||
{
|
||||
viewportOptions: {
|
||||
viewportType: 'volume',
|
||||
},
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should handle viewports without viewportOptions', () => {
|
||||
const mockUpdatedViewports = [
|
||||
{
|
||||
@@ -255,70 +218,22 @@ describe('getUpdatedViewportsForSegmentation', () => {
|
||||
it('should handle getViewportsRequireUpdate returning null', () => {
|
||||
mockHangingProtocolService.getViewportsRequireUpdate.mockReturnValue(null);
|
||||
|
||||
expect(() => getUpdatedViewportsForSegmentation(defaultParameters)).toThrow();
|
||||
expect(getUpdatedViewportsForSegmentation(defaultParameters)).toEqual(null);
|
||||
});
|
||||
|
||||
it('should handle mixed viewport types including volume3d', () => {
|
||||
const mockUpdatedViewports = [
|
||||
{
|
||||
viewportOptions: {
|
||||
viewportType: 'stack',
|
||||
},
|
||||
},
|
||||
{
|
||||
viewportOptions: {
|
||||
viewportType: 'volume3d',
|
||||
},
|
||||
},
|
||||
{
|
||||
viewportOptions: {
|
||||
viewportType: 'volume3d',
|
||||
},
|
||||
},
|
||||
{
|
||||
viewportOptions: {
|
||||
viewportType: 'orthogonal',
|
||||
},
|
||||
},
|
||||
{ viewportOptions: { viewportType: 'stack' } },
|
||||
{ viewportOptions: { viewportType: 'volume3d' } },
|
||||
{ viewportOptions: { viewportType: 'volume3d' } },
|
||||
{ viewportOptions: { viewportType: 'orthogonal' } },
|
||||
];
|
||||
|
||||
mockHangingProtocolService.getViewportsRequireUpdate.mockReturnValue(mockUpdatedViewports);
|
||||
|
||||
const result = getUpdatedViewportsForSegmentation(defaultParameters);
|
||||
|
||||
expect(result).toEqual([
|
||||
{
|
||||
viewportOptions: {
|
||||
viewportType: 'stack',
|
||||
},
|
||||
},
|
||||
{
|
||||
viewportOptions: {
|
||||
viewportType: 'orthogonal',
|
||||
},
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should handle all volume3d viewports', () => {
|
||||
const mockUpdatedViewports = [
|
||||
{
|
||||
viewportOptions: {
|
||||
viewportType: 'volume3d',
|
||||
},
|
||||
},
|
||||
{
|
||||
viewportOptions: {
|
||||
viewportType: 'volume3d',
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
mockHangingProtocolService.getViewportsRequireUpdate.mockReturnValue(mockUpdatedViewports);
|
||||
|
||||
const result = getUpdatedViewportsForSegmentation(defaultParameters);
|
||||
|
||||
expect(result).toEqual([]);
|
||||
expect(result).toEqual(mockUpdatedViewports);
|
||||
});
|
||||
|
||||
it('should handle viewports with undefined viewportType', () => {
|
||||
@@ -328,6 +243,11 @@ describe('getUpdatedViewportsForSegmentation', () => {
|
||||
viewportType: undefined,
|
||||
},
|
||||
},
|
||||
{
|
||||
viewportOptions: {
|
||||
viewportType: 'volume3d',
|
||||
},
|
||||
},
|
||||
{
|
||||
viewportOptions: {
|
||||
someOtherProperty: 'value',
|
||||
|
||||
@@ -16,7 +16,7 @@ function getUpdatedViewportsForSegmentation({
|
||||
isHangingProtocolLayout
|
||||
);
|
||||
|
||||
return updatedViewports.filter(v => v.viewportOptions?.viewportType !== 'volume3d');
|
||||
return updatedViewports;
|
||||
}
|
||||
|
||||
const getTargetViewport = ({ viewportId, viewportGridService }) => {
|
||||
|
||||
@@ -10,6 +10,7 @@ import promptHydrationDialog, {
|
||||
HydrationSRResult,
|
||||
} from './promptHydrationDialog';
|
||||
import { getCenterExtent } from './getCenterExtent';
|
||||
import { createSegmentationForViewport } from './createSegmentationForViewport';
|
||||
|
||||
const utils = {
|
||||
handleSegmentChange,
|
||||
@@ -18,6 +19,7 @@ const utils = {
|
||||
setupSegmentationModifiedHandler,
|
||||
promptHydrationDialog,
|
||||
getCenterExtent,
|
||||
createSegmentationForViewport,
|
||||
};
|
||||
|
||||
export type { HydrationDialogProps, HydrationCallback, HydrationSRResult };
|
||||
|
||||
@@ -84,7 +84,7 @@ export default function interleaveCenterLoader({
|
||||
// the imageIds and save them in AllRequests for later use
|
||||
const AllRequests = [];
|
||||
volumes.forEach(volume => {
|
||||
const requests = volume.getImageLoadRequests();
|
||||
const requests = volume.getImageLoadRequests?.() ?? [];
|
||||
|
||||
if (!requests.length || !requests[0] || !requests[0].imageId) {
|
||||
return;
|
||||
|
||||
@@ -83,7 +83,7 @@ export default function interleaveTopToBottom({
|
||||
// the imageIds and save them in AllRequests for later use
|
||||
const AllRequests = [];
|
||||
volumes.forEach(volume => {
|
||||
const requests = volume.getImageLoadRequests();
|
||||
const requests = volume.getImageLoadRequests?.() ?? [];
|
||||
|
||||
if (!requests?.[0]?.imageId) {
|
||||
return;
|
||||
|
||||
@@ -107,8 +107,8 @@ function getMappedAnnotations(annotation, displaySetService) {
|
||||
const displaySet = displaySetService.getDisplaySetsForSeries(SeriesInstanceUID)[0];
|
||||
|
||||
const { SeriesNumber } = displaySet;
|
||||
const { value } = targetStats;
|
||||
const unit = 'HU';
|
||||
const { value, modalityUnit } = targetStats;
|
||||
const unit = modalityUnit || '';
|
||||
|
||||
annotations.push({
|
||||
SeriesInstanceUID,
|
||||
|
||||
-1
@@ -13,7 +13,6 @@ const supportedTools = [
|
||||
'LivewireContour',
|
||||
'UltrasoundDirectionalTool',
|
||||
'UltrasoundPleuraBLineTool',
|
||||
'SCOORD3DPoint',
|
||||
'SegmentBidirectional',
|
||||
];
|
||||
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
import { cache, metaData } from '@cornerstonejs/core';
|
||||
import { Types as cstTypes } from '@cornerstonejs/tools';
|
||||
|
||||
/**
|
||||
* Checks if a labelmap segmentation has exportable data
|
||||
* A label map is exportable if it has any pixel data and
|
||||
* it is referenced by a display set that is reconstructable.
|
||||
*
|
||||
* @param labelmap - The labelmap representation data
|
||||
* @param displaySetService - The display set service instance
|
||||
* @returns boolean - Whether the labelmap has exportable data
|
||||
*/
|
||||
export const hasExportableLabelMapData = (
|
||||
labelmap: cstTypes.LabelmapSegmentationData | undefined,
|
||||
displaySetService: any
|
||||
): boolean => {
|
||||
if (!labelmap) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const imageIds = labelmap?.imageIds;
|
||||
if (!imageIds?.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const hasPixelData = imageIds.some(imageId => {
|
||||
const pixelData = cache.getImage(imageId)?.getPixelData();
|
||||
if (!pixelData) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (let i = 0; i < pixelData.length; i++) {
|
||||
if (pixelData[i] !== 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (!hasPixelData) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const referencedImageIds = labelmap?.referencedImageIds;
|
||||
|
||||
if (!referencedImageIds) {
|
||||
return false;
|
||||
}
|
||||
const firstImageId = referencedImageIds[0];
|
||||
const instance = metaData.get('instance', firstImageId);
|
||||
|
||||
if (!instance) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const SOPInstanceUID = instance.SOPInstanceUID || instance.SopInstanceUID;
|
||||
const SeriesInstanceUID = instance.SeriesInstanceUID;
|
||||
const displaySet = displaySetService.getDisplaySetForSOPInstanceUID(
|
||||
SOPInstanceUID,
|
||||
SeriesInstanceUID
|
||||
);
|
||||
|
||||
return !!displaySet;
|
||||
};
|
||||
|
||||
/**
|
||||
* Checks if a contour segmentation has exportable data
|
||||
* A contour is exportable if it has any contour annotation/geometry data.
|
||||
*
|
||||
* @param contour - The contour representation data
|
||||
* @returns boolean - Whether the contour has exportable data
|
||||
*/
|
||||
export const hasExportableContourData = (
|
||||
contour: cstTypes.ContourSegmentationData | undefined
|
||||
): boolean => {
|
||||
if (!contour) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const contourAnnotationUIDsMap = contour?.annotationUIDsMap;
|
||||
|
||||
if (!contourAnnotationUIDsMap) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return contourAnnotationUIDsMap.size > 0;
|
||||
};
|
||||
@@ -1,5 +1,7 @@
|
||||
import * as cornerstoneTools from '@cornerstonejs/tools';
|
||||
import { updateSegmentationStats } from './updateSegmentationStats';
|
||||
import { useSelectedSegmentationsForViewportStore } from '../stores';
|
||||
import { SegmentationRepresentations } from '@cornerstonejs/tools/enums';
|
||||
|
||||
/**
|
||||
* Sets up the handler for segmentation data modification events
|
||||
@@ -110,3 +112,49 @@ export function setupSegmentationModifiedHandler({ segmentationService }) {
|
||||
|
||||
return { unsubscribe };
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up auto tab switching for when the first segmentation is added into the viewer.
|
||||
*/
|
||||
export function setUpSelectedSegmentationsForViewportHandler({ segmentationService }) {
|
||||
const selectedSegmentationsForViewportEvents = [
|
||||
segmentationService.EVENTS.SEGMENTATION_MODIFIED,
|
||||
segmentationService.EVENTS.SEGMENTATION_REPRESENTATION_MODIFIED,
|
||||
];
|
||||
|
||||
const unsubscribeSelectedSegmentationsForViewportEvents = selectedSegmentationsForViewportEvents
|
||||
.map(eventName =>
|
||||
segmentationService.subscribe(eventName, event => {
|
||||
const { viewportId } = event;
|
||||
|
||||
if (!viewportId) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { selectedSegmentationsForViewport, setSelectedSegmentationsForViewport } =
|
||||
useSelectedSegmentationsForViewportStore.getState();
|
||||
|
||||
const representations = segmentationService.getSegmentationRepresentations(viewportId);
|
||||
|
||||
const activeRepresentation = representations.find(representation => representation.active);
|
||||
|
||||
const typeToSegmentationIdMap =
|
||||
selectedSegmentationsForViewport[viewportId] ??
|
||||
new Map<SegmentationRepresentations, string>();
|
||||
|
||||
if (activeRepresentation) {
|
||||
typeToSegmentationIdMap.set(
|
||||
activeRepresentation.type,
|
||||
activeRepresentation.segmentationId
|
||||
);
|
||||
} else {
|
||||
typeToSegmentationIdMap.clear();
|
||||
}
|
||||
|
||||
setSelectedSegmentationsForViewport(viewportId, typeToSegmentationIdMap);
|
||||
})
|
||||
)
|
||||
.map(subscription => subscription.unsubscribe);
|
||||
|
||||
return { unsubscribeSelectedSegmentationsForViewportEvents };
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
import * as csTools from '@cornerstonejs/tools';
|
||||
|
||||
import { eventTarget } from '@cornerstonejs/core';
|
||||
|
||||
const { Enums: csToolsEnums } = csTools;
|
||||
|
||||
const _autoAcceptAnnotationInterpolationHandler = evt => {
|
||||
const { element } = evt.detail;
|
||||
csTools.utilities.contours.acceptAutogeneratedInterpolations(element, {});
|
||||
};
|
||||
|
||||
export const setUpAnnotationEventHandlers = (): [() => void] => {
|
||||
eventTarget.addEventListener(
|
||||
csToolsEnums.Events.ANNOTATION_INTERPOLATION_PROCESS_COMPLETED,
|
||||
_autoAcceptAnnotationInterpolationHandler
|
||||
);
|
||||
|
||||
const annotationInterpolationProcessCompletedUnsubscribe = () => {
|
||||
eventTarget.removeEventListener(
|
||||
csToolsEnums.Events.ANNOTATION_INTERPOLATION_PROCESS_COMPLETED,
|
||||
_autoAcceptAnnotationInterpolationHandler
|
||||
);
|
||||
};
|
||||
|
||||
return [annotationInterpolationProcessCompletedUnsubscribe];
|
||||
};
|
||||
@@ -2,11 +2,13 @@ import { setUpSegmentationEventHandlers } from './setUpSegmentationEventHandlers
|
||||
import {
|
||||
setupSegmentationDataModifiedHandler,
|
||||
setupSegmentationModifiedHandler,
|
||||
setUpSelectedSegmentationsForViewportHandler,
|
||||
} from './segmentationHandlers';
|
||||
|
||||
jest.mock('./segmentationHandlers', () => ({
|
||||
setupSegmentationDataModifiedHandler: jest.fn(),
|
||||
setupSegmentationModifiedHandler: jest.fn(),
|
||||
setUpSelectedSegmentationsForViewportHandler: jest.fn(),
|
||||
}));
|
||||
|
||||
describe('setUpSegmentationEventHandlers', () => {
|
||||
@@ -38,6 +40,7 @@ describe('setUpSegmentationEventHandlers', () => {
|
||||
const mockUnsubscribeDataModified = jest.fn();
|
||||
const mockUnsubscribeModified = jest.fn();
|
||||
const mockUnsubscribeCreated = jest.fn();
|
||||
const mockUnsubscribeSelectedSegmentationsForViewportEvents = [jest.fn(), jest.fn()];
|
||||
|
||||
const defaultParameters = {
|
||||
servicesManager: mockServicesManager as unknown as AppTypes.ServicesManager,
|
||||
@@ -52,6 +55,10 @@ describe('setUpSegmentationEventHandlers', () => {
|
||||
(setupSegmentationModifiedHandler as jest.Mock).mockReturnValue({
|
||||
unsubscribe: mockUnsubscribeModified,
|
||||
});
|
||||
(setUpSelectedSegmentationsForViewportHandler as jest.Mock).mockReturnValue({
|
||||
unsubscribeSelectedSegmentationsForViewportEvents:
|
||||
mockUnsubscribeSelectedSegmentationsForViewportEvents,
|
||||
});
|
||||
mockSegmentationService.subscribe.mockReturnValue({
|
||||
unsubscribe: mockUnsubscribeCreated,
|
||||
});
|
||||
@@ -92,6 +99,7 @@ describe('setUpSegmentationEventHandlers', () => {
|
||||
mockUnsubscribeDataModified,
|
||||
mockUnsubscribeModified,
|
||||
mockUnsubscribeCreated,
|
||||
...mockUnsubscribeSelectedSegmentationsForViewportEvents,
|
||||
],
|
||||
});
|
||||
});
|
||||
@@ -117,6 +125,7 @@ describe('setUpSegmentationEventHandlers', () => {
|
||||
|
||||
it('should create and add display set when segmentation is added and no displaySet exists', () => {
|
||||
const mockSegmentation = {
|
||||
label: 'Test Segmentation Label',
|
||||
cachedStats: {
|
||||
info: 'Test Segmentation Label',
|
||||
},
|
||||
@@ -143,12 +152,12 @@ describe('setUpSegmentationEventHandlers', () => {
|
||||
displaySetInstanceUID: 'test-segmentation-id',
|
||||
SOPClassUID: '1.2.840.10008.5.1.4.1.1.66.4',
|
||||
SOPClassHandlerId: '@ohif/extension-cornerstone-dicom-seg.sopClassHandlerModule.dicom-seg',
|
||||
SeriesDescription: mockSegmentation.cachedStats.info,
|
||||
SeriesDescription: mockSegmentation.label,
|
||||
Modality: 'SEG',
|
||||
numImageFrames: mockSegmentation.representationData.Labelmap.imageIds.length,
|
||||
imageIds: mockSegmentation.representationData.Labelmap.imageIds,
|
||||
isOverlayDisplaySet: true,
|
||||
label: mockSegmentation.cachedStats.info,
|
||||
label: mockSegmentation.label,
|
||||
madeInClient: true,
|
||||
segmentationId: 'test-segmentation-id',
|
||||
isDerived: true,
|
||||
@@ -157,6 +166,7 @@ describe('setUpSegmentationEventHandlers', () => {
|
||||
|
||||
it('should handle displaySet undefined when segmentation is added', () => {
|
||||
const mockSegmentation = {
|
||||
label: 'Test Segmentation Label',
|
||||
cachedStats: {
|
||||
info: 'Test Segmentation Label',
|
||||
},
|
||||
@@ -182,12 +192,12 @@ describe('setUpSegmentationEventHandlers', () => {
|
||||
displaySetInstanceUID: 'test-segmentation-id',
|
||||
SOPClassUID: '1.2.840.10008.5.1.4.1.1.66.4',
|
||||
SOPClassHandlerId: '@ohif/extension-cornerstone-dicom-seg.sopClassHandlerModule.dicom-seg',
|
||||
SeriesDescription: mockSegmentation.cachedStats.info,
|
||||
SeriesDescription: mockSegmentation.label,
|
||||
Modality: 'SEG',
|
||||
numImageFrames: mockSegmentation.representationData.Labelmap.imageIds.length,
|
||||
imageIds: mockSegmentation.representationData.Labelmap.imageIds,
|
||||
isOverlayDisplaySet: true,
|
||||
label: mockSegmentation.cachedStats.info,
|
||||
label: mockSegmentation.label,
|
||||
madeInClient: true,
|
||||
segmentationId: 'test-segmentation-id',
|
||||
isDerived: true,
|
||||
@@ -196,6 +206,7 @@ describe('setUpSegmentationEventHandlers', () => {
|
||||
|
||||
it('should handle empty imageIds array', () => {
|
||||
const mockSegmentation = {
|
||||
label: 'Empty Segmentation',
|
||||
cachedStats: {
|
||||
info: 'Empty Segmentation',
|
||||
},
|
||||
@@ -220,12 +231,12 @@ describe('setUpSegmentationEventHandlers', () => {
|
||||
displaySetInstanceUID: 'empty-segmentation-id',
|
||||
SOPClassUID: '1.2.840.10008.5.1.4.1.1.66.4',
|
||||
SOPClassHandlerId: '@ohif/extension-cornerstone-dicom-seg.sopClassHandlerModule.dicom-seg',
|
||||
SeriesDescription: mockSegmentation.cachedStats.info,
|
||||
SeriesDescription: mockSegmentation.label,
|
||||
Modality: 'SEG',
|
||||
numImageFrames: 0,
|
||||
imageIds: [],
|
||||
isOverlayDisplaySet: true,
|
||||
label: mockSegmentation.cachedStats.info,
|
||||
label: mockSegmentation.label,
|
||||
madeInClient: true,
|
||||
segmentationId: 'empty-segmentation-id',
|
||||
isDerived: true,
|
||||
@@ -234,6 +245,7 @@ describe('setUpSegmentationEventHandlers', () => {
|
||||
|
||||
it('should handle different segmentation label values', () => {
|
||||
const mockSegmentation = {
|
||||
label: 'Custom Label Text',
|
||||
cachedStats: {
|
||||
info: 'Custom Label Text',
|
||||
},
|
||||
@@ -272,6 +284,7 @@ describe('setUpSegmentationEventHandlers', () => {
|
||||
|
||||
it('should handle multiple segmentation events', () => {
|
||||
const mockSegmentation1 = {
|
||||
label: 'Segmentation 1',
|
||||
cachedStats: {
|
||||
info: 'Segmentation 1',
|
||||
},
|
||||
@@ -283,6 +296,7 @@ describe('setUpSegmentationEventHandlers', () => {
|
||||
};
|
||||
|
||||
const mockSegmentation2 = {
|
||||
label: 'Segmentation 2',
|
||||
cachedStats: {
|
||||
info: 'Segmentation 2',
|
||||
},
|
||||
@@ -310,12 +324,12 @@ describe('setUpSegmentationEventHandlers', () => {
|
||||
displaySetInstanceUID: 'segmentation-1',
|
||||
SOPClassUID: '1.2.840.10008.5.1.4.1.1.66.4',
|
||||
SOPClassHandlerId: '@ohif/extension-cornerstone-dicom-seg.sopClassHandlerModule.dicom-seg',
|
||||
SeriesDescription: mockSegmentation1.cachedStats.info,
|
||||
SeriesDescription: mockSegmentation1.label,
|
||||
Modality: 'SEG',
|
||||
numImageFrames: 1,
|
||||
imageIds: mockSegmentation1.representationData.Labelmap.imageIds,
|
||||
isOverlayDisplaySet: true,
|
||||
label: mockSegmentation1.cachedStats.info,
|
||||
label: mockSegmentation1.label,
|
||||
madeInClient: true,
|
||||
segmentationId: 'segmentation-1',
|
||||
isDerived: true,
|
||||
@@ -324,12 +338,12 @@ describe('setUpSegmentationEventHandlers', () => {
|
||||
displaySetInstanceUID: 'segmentation-2',
|
||||
SOPClassUID: '1.2.840.10008.5.1.4.1.1.66.4',
|
||||
SOPClassHandlerId: '@ohif/extension-cornerstone-dicom-seg.sopClassHandlerModule.dicom-seg',
|
||||
SeriesDescription: mockSegmentation2.cachedStats.info,
|
||||
SeriesDescription: mockSegmentation2.label,
|
||||
Modality: 'SEG',
|
||||
numImageFrames: 2,
|
||||
imageIds: mockSegmentation2.representationData.Labelmap.imageIds,
|
||||
isOverlayDisplaySet: true,
|
||||
label: mockSegmentation2.cachedStats.info,
|
||||
label: mockSegmentation2.label,
|
||||
madeInClient: true,
|
||||
segmentationId: 'segmentation-2',
|
||||
isDerived: true,
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
import { Enums as csToolsEnums } from '@cornerstonejs/tools';
|
||||
|
||||
import {
|
||||
setUpSelectedSegmentationsForViewportHandler,
|
||||
setupSegmentationDataModifiedHandler,
|
||||
setupSegmentationModifiedHandler,
|
||||
} from './segmentationHandlers';
|
||||
@@ -27,8 +30,10 @@ export const setUpSegmentationEventHandlers = ({ servicesManager, commandsManage
|
||||
}
|
||||
|
||||
const segmentation = segmentationService.getSegmentation(segmentationId);
|
||||
const label = segmentation.cachedStats.info;
|
||||
const imageIds = segmentation.representationData.Labelmap.imageIds;
|
||||
const label = segmentation.label;
|
||||
const imageIds =
|
||||
segmentation.representationData?.Labelmap?.imageIds ??
|
||||
segmentation.representationData?.Contour?.imageIds;
|
||||
|
||||
// Create a display set for the segmentation
|
||||
const segmentationDisplaySet = {
|
||||
@@ -36,7 +41,9 @@ export const setUpSegmentationEventHandlers = ({ servicesManager, commandsManage
|
||||
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',
|
||||
Modality: segmentation.representationData[csToolsEnums.SegmentationRepresentations.Contour]
|
||||
? 'RTSTRUCT'
|
||||
: 'SEG',
|
||||
numImageFrames: imageIds.length,
|
||||
imageIds,
|
||||
isOverlayDisplaySet: true,
|
||||
@@ -50,10 +57,16 @@ export const setUpSegmentationEventHandlers = ({ servicesManager, commandsManage
|
||||
}
|
||||
);
|
||||
|
||||
const { unsubscribeSelectedSegmentationsForViewportEvents } =
|
||||
setUpSelectedSegmentationsForViewportHandler({
|
||||
segmentationService,
|
||||
});
|
||||
|
||||
const unsubscriptions = [
|
||||
unsubscribeSegmentationDataModifiedHandler,
|
||||
unsubscribeSegmentationModifiedHandler,
|
||||
unsubscribeSegmentationCreated,
|
||||
...unsubscribeSelectedSegmentationsForViewportEvents,
|
||||
];
|
||||
|
||||
return { unsubscriptions };
|
||||
|
||||
@@ -84,6 +84,10 @@ function groupViewportsByModality(
|
||||
return viewportsArray.reduce((acc, viewport) => {
|
||||
const { displaySetInstanceUIDs } = viewport;
|
||||
// Todo: add proper fusion support
|
||||
// Fix: Skip processing if the viewport is empty.
|
||||
if (!displaySetInstanceUIDs?.length) {
|
||||
return acc;
|
||||
}
|
||||
const displaySetInstanceUID = displaySetInstanceUIDs[0];
|
||||
const displaySet = displaySetService.getDisplaySetByUID(displaySetInstanceUID);
|
||||
|
||||
|
||||
@@ -348,20 +348,4 @@ describe('Transitions Module', () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Performance Validation', () => {
|
||||
it('should handle many rapid calls without issues', () => {
|
||||
const startTime = performance.now();
|
||||
|
||||
for (let i = 0; i < 1000; i++) {
|
||||
const t = i / 1000;
|
||||
linear(t);
|
||||
easeInOut(t, 0.1);
|
||||
ease(t, 0.2, 1.5);
|
||||
}
|
||||
|
||||
const endTime = performance.now();
|
||||
expect(endTime - startTime).toBeLessThan(10);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -3,6 +3,286 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [3.12.0-beta.97](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.96...v3.12.0-beta.97) (2025-12-03)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-default
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.96](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.95...v3.12.0-beta.96) (2025-12-02)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-default
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.95](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.94...v3.12.0-beta.95) (2025-12-02)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-default
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.94](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.93...v3.12.0-beta.94) (2025-12-02)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-default
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.93](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.92...v3.12.0-beta.93) (2025-11-25)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-default
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.92](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.91...v3.12.0-beta.92) (2025-11-24)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-default
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.91](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.90...v3.12.0-beta.91) (2025-11-21)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-default
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.90](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.89...v3.12.0-beta.90) (2025-11-21)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-default
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.89](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.88...v3.12.0-beta.89) (2025-11-20)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-default
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.88](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.87...v3.12.0-beta.88) (2025-11-20)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-default
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.87](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.86...v3.12.0-beta.87) (2025-11-19)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-default
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.86](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.85...v3.12.0-beta.86) (2025-11-13)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-default
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.85](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.84...v3.12.0-beta.85) (2025-11-11)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-default
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.84](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.83...v3.12.0-beta.84) (2025-11-10)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-default
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.83](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.82...v3.12.0-beta.83) (2025-11-06)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-default
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.82](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.81...v3.12.0-beta.82) (2025-11-05)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-default
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.81](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.80...v3.12.0-beta.81) (2025-11-05)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-default
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.80](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.79...v3.12.0-beta.80) (2025-11-05)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-default
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.79](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.78...v3.12.0-beta.79) (2025-10-30)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-default
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.78](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.77...v3.12.0-beta.78) (2025-10-29)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-default
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.77](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.76...v3.12.0-beta.77) (2025-10-28)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-default
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.76](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.75...v3.12.0-beta.76) (2025-10-27)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-default
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.75](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.74...v3.12.0-beta.75) (2025-10-24)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-default
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.74](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.73...v3.12.0-beta.74) (2025-10-23)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-default
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.73](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.72...v3.12.0-beta.73) (2025-10-22)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-default
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.72](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.71...v3.12.0-beta.72) (2025-10-22)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-default
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.71](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.70...v3.12.0-beta.71) (2025-10-21)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-default
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.70](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.69...v3.12.0-beta.70) (2025-10-20)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-default
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.69](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.68...v3.12.0-beta.69) (2025-10-20)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-default
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.68](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.67...v3.12.0-beta.68) (2025-10-18)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-default
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.67](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.66...v3.12.0-beta.67) (2025-10-17)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-default
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.66](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.65...v3.12.0-beta.66) (2025-10-17)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-default
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.65](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.64...v3.12.0-beta.65) (2025-10-17)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-default
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.64](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.63...v3.12.0-beta.64) (2025-10-15)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-default
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.63](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.62...v3.12.0-beta.63) (2025-10-15)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-default
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.12.0-beta.62](https://github.com/OHIF/Viewers/compare/v3.12.0-beta.61...v3.12.0-beta.62) (2025-10-15)
|
||||
|
||||
**Note:** Version bump only for package @ohif/extension-default
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@ohif/extension-default",
|
||||
"version": "3.12.0-beta.62",
|
||||
"version": "3.12.0-beta.97",
|
||||
"description": "Common/default features and functionality for basic image viewing",
|
||||
"author": "OHIF Core Team",
|
||||
"license": "MIT",
|
||||
@@ -34,9 +34,9 @@
|
||||
"test:unit:ci": "jest --ci --runInBand --collectCoverage --passWithNoTests"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@ohif/core": "3.12.0-beta.62",
|
||||
"@ohif/i18n": "3.12.0-beta.62",
|
||||
"dcmjs": "0.43.1",
|
||||
"@ohif/core": "3.12.0-beta.97",
|
||||
"@ohif/i18n": "3.12.0-beta.97",
|
||||
"dcmjs": "0.45.0",
|
||||
"dicomweb-client": "0.10.4",
|
||||
"prop-types": "15.8.1",
|
||||
"react": "18.3.1",
|
||||
|
||||
@@ -28,7 +28,7 @@ const SidePanelWithServices = ({
|
||||
onClose,
|
||||
...props
|
||||
}: SidePanelWithServicesProps) => {
|
||||
const panelService = servicesManager?.services?.panelService;
|
||||
const { panelService, toolbarService, viewportGridService } = servicesManager.services;
|
||||
|
||||
// Tracks whether this SidePanel has been opened at least once since this SidePanel was inserted into the DOM.
|
||||
// Thus going to the Study List page and back to the viewer resets this flag for a SidePanel.
|
||||
@@ -37,9 +37,15 @@ const SidePanelWithServices = ({
|
||||
const [closedManually, setClosedManually] = useState(false);
|
||||
const [tabs, setTabs] = useState(tabsProp ?? panelService.getPanels(side));
|
||||
|
||||
const handleActiveTabIndexChange = useCallback(({ activeTabIndex }) => {
|
||||
setActiveTabIndex(activeTabIndex);
|
||||
}, []);
|
||||
const handleActiveTabIndexChange = useCallback(
|
||||
({ activeTabIndex }) => {
|
||||
const { activeViewportId: viewportId } = viewportGridService.getState();
|
||||
toolbarService.refreshToolbarState({ viewportId });
|
||||
|
||||
setActiveTabIndex(activeTabIndex);
|
||||
},
|
||||
[toolbarService, viewportGridService]
|
||||
);
|
||||
|
||||
const handleOpen = useCallback(() => {
|
||||
setSidePanelExpanded(true);
|
||||
|
||||
@@ -16,6 +16,7 @@ import { retrieveStudyMetadata, deleteStudyMetadataPromise } from './retrieveStu
|
||||
import StaticWadoClient from './utils/StaticWadoClient';
|
||||
import getDirectURL from '../utils/getDirectURL';
|
||||
import { fixBulkDataURI } from './utils/fixBulkDataURI';
|
||||
import {HeadersInterface} from '@ohif/core/src/types/RequestHeaders';
|
||||
|
||||
const { DicomMetaDictionary, DicomDict } = dcmjs.data;
|
||||
|
||||
@@ -97,6 +98,20 @@ export type BulkDataURIConfig = {
|
||||
relativeResolution?: 'studies' | 'series';
|
||||
};
|
||||
|
||||
/**
|
||||
* The header options are the options passed into the generateWadoHeader
|
||||
* command. This takes an extensible set of attributes to allow future enhancements.
|
||||
*/
|
||||
export interface HeaderOptions {
|
||||
includeTransferSyntax?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Metadata and some other requests don't permit the transfer syntax to be included,
|
||||
* so pass in the excludeTransferSyntax parameter.
|
||||
*/
|
||||
export const excludeTransferSyntax: HeaderOptions = { includeTransferSyntax: false };
|
||||
|
||||
/**
|
||||
* Creates a DICOM Web API based on the provided configuration.
|
||||
*
|
||||
@@ -128,7 +143,7 @@ function createDicomWebApi(dicomWebConfig: DicomWebConfig, servicesManager) {
|
||||
dicomWebConfigCopy = JSON.parse(JSON.stringify(dicomWebConfig));
|
||||
|
||||
getAuthorizationHeader = () => {
|
||||
const xhrRequestHeaders = {};
|
||||
const xhrRequestHeaders: HeadersInterface = {};
|
||||
const authHeaders = userAuthenticationService.getAuthorizationHeader();
|
||||
if (authHeaders && authHeaders.Authorization) {
|
||||
xhrRequestHeaders.Authorization = authHeaders.Authorization;
|
||||
@@ -136,19 +151,33 @@ function createDicomWebApi(dicomWebConfig: DicomWebConfig, servicesManager) {
|
||||
return xhrRequestHeaders;
|
||||
};
|
||||
|
||||
generateWadoHeader = () => {
|
||||
/**
|
||||
* Generates the wado header for requesting resources from DICOMweb.
|
||||
* These are classified into those that are dependent on the transfer syntax
|
||||
* and those that aren't, as defined by the include transfer syntax attribute.
|
||||
*/
|
||||
generateWadoHeader = (options: HeaderOptions): HeadersInterface => {
|
||||
const authorizationHeader = getAuthorizationHeader();
|
||||
//Generate accept header depending on config params
|
||||
const formattedAcceptHeader = utils.generateAcceptHeader(
|
||||
dicomWebConfig.acceptHeader,
|
||||
dicomWebConfig.requestTransferSyntaxUID,
|
||||
dicomWebConfig.omitQuotationForMultipartRequest
|
||||
);
|
||||
|
||||
return {
|
||||
...authorizationHeader,
|
||||
Accept: formattedAcceptHeader,
|
||||
};
|
||||
if (options?.includeTransferSyntax!==false) {
|
||||
//Generate accept header depending on config params
|
||||
const formattedAcceptHeader = utils.generateAcceptHeader(
|
||||
dicomWebConfig.acceptHeader,
|
||||
dicomWebConfig.requestTransferSyntaxUID,
|
||||
dicomWebConfig.omitQuotationForMultipartRequest
|
||||
);
|
||||
return {
|
||||
...authorizationHeader,
|
||||
Accept: formattedAcceptHeader,
|
||||
};
|
||||
} else {
|
||||
// The base header will be included in the request. We simply skip customization options around
|
||||
// transfer syntaxes and whether the request is multipart. In other words, a request in
|
||||
// which the server expects Accept: application/dicom+json will still include that in the
|
||||
// header.
|
||||
return {
|
||||
...authorizationHeader
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
qidoConfig = {
|
||||
@@ -410,7 +439,7 @@ function createDicomWebApi(dicomWebConfig: DicomWebConfig, servicesManager) {
|
||||
madeInClient
|
||||
) => {
|
||||
const enableStudyLazyLoad = false;
|
||||
wadoDicomWebClient.headers = generateWadoHeader();
|
||||
wadoDicomWebClient.headers = generateWadoHeader(excludeTransferSyntax);
|
||||
// data is all SOPInstanceUIDs
|
||||
const data = await retrieveStudyMetadata(
|
||||
wadoDicomWebClient,
|
||||
@@ -484,7 +513,7 @@ function createDicomWebApi(dicomWebConfig: DicomWebConfig, servicesManager) {
|
||||
returnPromises = false
|
||||
) => {
|
||||
const enableStudyLazyLoad = true;
|
||||
wadoDicomWebClient.headers = generateWadoHeader();
|
||||
wadoDicomWebClient.headers = generateWadoHeader(excludeTransferSyntax);
|
||||
// Get Series
|
||||
const { preLoadData: seriesSummaryMetadata, promises: seriesPromises } =
|
||||
await retrieveStudyMetadata(
|
||||
|
||||
@@ -7,6 +7,7 @@ import { PanelStudyBrowserHeader } from './PanelStudyBrowserHeader';
|
||||
import { defaultActionIcons } from './constants';
|
||||
import MoreDropdownMenu from '../../Components/MoreDropdownMenu';
|
||||
import { CallbackCustomization } from 'platform/core/src/types';
|
||||
import { type TabsProps } from '@ohif/core/src/utils/createStudyBrowserTabs';
|
||||
|
||||
const { sortStudyInstances, formatDate, createStudyBrowserTabs } = utils;
|
||||
|
||||
@@ -27,7 +28,8 @@ function PanelStudyBrowser({
|
||||
const { servicesManager, commandsManager, extensionManager } = useSystem();
|
||||
const { displaySetService, customizationService } = servicesManager.services;
|
||||
const navigate = useNavigate();
|
||||
const studyMode = customizationService.getCustomization('studyBrowser.studyMode') || 'all';
|
||||
const studyMode =
|
||||
(customizationService.getCustomization('studyBrowser.studyMode') as string) || 'all';
|
||||
|
||||
const internalImageViewer = useImageViewer();
|
||||
const StudyInstanceUIDs = internalImageViewer.StudyInstanceUIDs;
|
||||
@@ -382,8 +384,12 @@ function PanelStudyBrowser({
|
||||
}
|
||||
|
||||
const displaySetInstanceUID = jumpToDisplaySet;
|
||||
// Set the activeTabName and expand the study
|
||||
const thumbnailLocation = _findTabAndStudyOfDisplaySet(displaySetInstanceUID, tabs);
|
||||
// It is possible to navigate to a study not currently in view
|
||||
const thumbnailLocation = _findTabAndStudyOfDisplaySet(
|
||||
displaySetInstanceUID,
|
||||
tabs,
|
||||
activeTabName
|
||||
);
|
||||
if (!thumbnailLocation) {
|
||||
return;
|
||||
}
|
||||
@@ -488,7 +494,7 @@ function _mapDisplaySets(displaySets, displaySetLoadingState, thumbnailImageSrcM
|
||||
seriesNumber: ds.SeriesNumber,
|
||||
modality: ds.Modality,
|
||||
seriesDate: formatDate(ds.SeriesDate),
|
||||
numInstances: ds.numImageFrames,
|
||||
numInstances: ds.numImageFrames ?? ds.instances?.length,
|
||||
loadingProgress,
|
||||
countIcon: ds.countIcon,
|
||||
messages: ds.messages,
|
||||
@@ -532,23 +538,23 @@ function getImageIdForThumbnail(displaySet, imageIds) {
|
||||
return imageId;
|
||||
}
|
||||
|
||||
function _findTabAndStudyOfDisplaySet(displaySetInstanceUID, tabs) {
|
||||
for (let t = 0; t < tabs.length; t++) {
|
||||
const { studies } = tabs[t];
|
||||
function _findTabAndStudyOfDisplaySet(
|
||||
displaySetInstanceUID: string,
|
||||
tabs: TabsProps,
|
||||
currentTabName: string
|
||||
) {
|
||||
const current = tabs.find(tab => tab.name === currentTabName) || tabs[0];
|
||||
const biasedTabs = [current, ...tabs];
|
||||
|
||||
for (let s = 0; s < studies.length; s++) {
|
||||
const { displaySets } = studies[s];
|
||||
|
||||
for (let d = 0; d < displaySets.length; d++) {
|
||||
const displaySet = displaySets[d];
|
||||
|
||||
if (displaySet.displaySetInstanceUID === displaySetInstanceUID) {
|
||||
return {
|
||||
tabName: tabs[t].name,
|
||||
StudyInstanceUID: studies[s].studyInstanceUid,
|
||||
};
|
||||
}
|
||||
}
|
||||
for (let t = 0; t < biasedTabs.length; t++) {
|
||||
const study = biasedTabs[t].studies.find(study =>
|
||||
study.displaySets.find(ds => ds.displaySetInstanceUID === displaySetInstanceUID)
|
||||
);
|
||||
if (study) {
|
||||
return {
|
||||
tabName: biasedTabs[t].name,
|
||||
StudyInstanceUID: study.studyInstanceUid,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -14,6 +14,7 @@ function PanelStudyBrowserHeader({
|
||||
actionIcons: actionIcon[];
|
||||
updateActionIconValue: (actionIcon: actionIcon) => void;
|
||||
}) {
|
||||
// Button order: Settings button then List view mode (thumbnails vs. list)
|
||||
return (
|
||||
<>
|
||||
<div className="bg-muted flex h-[40px] select-none rounded-t p-2">
|
||||
|
||||
@@ -1,12 +1,39 @@
|
||||
import PROMPT_RESPONSES from '../utils/_shared/PROMPT_RESPONSES';
|
||||
|
||||
/**
|
||||
* Creates and shows a report dialog prompt.
|
||||
* The input for this is:
|
||||
* - `title` shown in the dialog
|
||||
* - `modality` being stored, used to query existing series
|
||||
* - `minSeriesNumber` is the start of new series of this modality type.
|
||||
* Will get set to 4000 if not determined by the modality
|
||||
* - predecessorImageId is the image id that this series was currently loaded
|
||||
* from. That allows defaulting the dialog to show the specified series instead
|
||||
* of always creating a new series.
|
||||
*
|
||||
* The response is:
|
||||
* - `value`, the default name of the object/series being created
|
||||
* - `dataSourceName`, where to store the object to
|
||||
* - `series`, is the series to store do, as referenced by a predecessorImageId value.
|
||||
* - `priorSeriesNumber` is the previously lowest series number at least minSeriesNumber
|
||||
* of all the seris of the given modality type.
|
||||
*
|
||||
* This should be provided to the DICOM encoder, which will get the predecessor
|
||||
* sequence from the metaData provider so that the saved series will replace
|
||||
* the existing instance in the same series.
|
||||
* This will be falsy for a new series.
|
||||
*/
|
||||
export default function CreateReportDialogPrompt({
|
||||
title = 'Create Report',
|
||||
modality = 'SR',
|
||||
minSeriesNumber = 0,
|
||||
predecessorImageId,
|
||||
extensionManager,
|
||||
servicesManager,
|
||||
}): Promise<{
|
||||
value: string;
|
||||
dataSourceName: string;
|
||||
priorSeriesNumber?: number;
|
||||
series: string;
|
||||
action: (typeof PROMPT_RESPONSES)[keyof typeof PROMPT_RESPONSES];
|
||||
}> {
|
||||
@@ -16,18 +43,33 @@ export default function CreateReportDialogPrompt({
|
||||
|
||||
const allowMultipleDataSources = window.config.allowMultiSelectExport;
|
||||
|
||||
return new Promise(function (resolve, reject) {
|
||||
minSeriesNumber ||=
|
||||
(modality === 'SR' && 3000) ||
|
||||
(modality === 'SEG' && 3100) ||
|
||||
(modality === 'RTSTRUCT' && 3200) ||
|
||||
4000;
|
||||
|
||||
return new Promise(function (resolve) {
|
||||
uiDialogService.show({
|
||||
id: 'report-dialog',
|
||||
title,
|
||||
content: ReportDialog,
|
||||
contentProps: {
|
||||
dataSources: allowMultipleDataSources ? dataSources : undefined,
|
||||
onSave: async ({ reportName, dataSource: selectedDataSource, series }) => {
|
||||
predecessorImageId,
|
||||
minSeriesNumber,
|
||||
modality,
|
||||
onSave: async ({
|
||||
reportName,
|
||||
dataSource: selectedDataSource,
|
||||
series,
|
||||
priorSeriesNumber,
|
||||
}) => {
|
||||
resolve({
|
||||
value: reportName,
|
||||
dataSourceName: selectedDataSource,
|
||||
series,
|
||||
priorSeriesNumber,
|
||||
action: PROMPT_RESPONSES.CREATE_REPORT,
|
||||
});
|
||||
},
|
||||
|
||||
@@ -1,9 +1,30 @@
|
||||
import React from 'react';
|
||||
import { useToolbar } from '@ohif/core';
|
||||
|
||||
/**
|
||||
* Props for the Toolbar component that renders a collection of toolbar buttons and/or button sections.
|
||||
*
|
||||
* @interface ToolbarProps
|
||||
*/
|
||||
interface ToolbarProps {
|
||||
/**
|
||||
* The section of buttons to display in the toolbar.
|
||||
* Common values include 'primary', 'secondary', 'tertiary', etc.
|
||||
* Defaults to 'primary' if not specified.
|
||||
*
|
||||
* @default 'primary'
|
||||
*/
|
||||
buttonSection?: string;
|
||||
|
||||
/**
|
||||
* The unique identifier of the viewport this toolbar is associated with.
|
||||
*/
|
||||
viewportId?: string;
|
||||
|
||||
/**
|
||||
* The numeric position or location of the toolbar.
|
||||
* Used for ordering and layout purposes in the UI.
|
||||
*/
|
||||
location?: number;
|
||||
}
|
||||
|
||||
@@ -60,7 +81,17 @@ export function Toolbar({ buttonSection = 'primary', viewportId, location }: Too
|
||||
/>
|
||||
);
|
||||
|
||||
return <div key={id}>{tool}</div>;
|
||||
return (
|
||||
<div
|
||||
key={id}
|
||||
// This wrapper div exists solely for React's key prop requirement during reconciliation.
|
||||
// We use display:contents to make it transparent to the layout engine (children appear
|
||||
// as direct children of the parent) while keeping it in the DOM for React's virtual DOM.
|
||||
className="contents"
|
||||
>
|
||||
{tool}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -11,28 +11,44 @@ type DataSource = {
|
||||
|
||||
type ReportDialogProps = {
|
||||
dataSources: DataSource[];
|
||||
modality?: string;
|
||||
predecessorImageId?: string;
|
||||
hide: () => void;
|
||||
onSave: (data: { reportName: string; dataSource: string | null; series: string | null }) => void;
|
||||
onSave: (data: {
|
||||
reportName: string;
|
||||
dataSource: string | null;
|
||||
series: string | null;
|
||||
priorSeriesNumber: number;
|
||||
}) => void;
|
||||
onCancel: () => void;
|
||||
};
|
||||
|
||||
function ReportDialog({ dataSources, hide, onSave, onCancel }: ReportDialogProps) {
|
||||
function ReportDialog({
|
||||
dataSources,
|
||||
modality = 'SR',
|
||||
predecessorImageId,
|
||||
minSeriesNumber = 3000,
|
||||
hide,
|
||||
onSave,
|
||||
onCancel,
|
||||
}: ReportDialogProps) {
|
||||
const { servicesManager } = useSystem();
|
||||
const [selectedDataSource, setSelectedDataSource] = useState<string | null>(
|
||||
dataSources?.[0]?.value ?? null
|
||||
);
|
||||
const [selectedSeries, setSelectedSeries] = useState<string | null>(null);
|
||||
const [reportName, setReportName] = useState('');
|
||||
|
||||
const { displaySetService } = servicesManager.services;
|
||||
|
||||
const [selectedSeries, setSelectedSeries] = useState<string | null>(predecessorImageId || null);
|
||||
const [reportName, setReportName] = useState('');
|
||||
|
||||
const seriesOptions = useMemo(() => {
|
||||
const displaySetsMap = displaySetService.getDisplaySetCache();
|
||||
const displaySets = Array.from(displaySetsMap.values());
|
||||
const options = displaySets
|
||||
.filter(ds => ds.Modality === 'SR')
|
||||
.filter(ds => ds.Modality === modality)
|
||||
.map(ds => ({
|
||||
value: ds.SeriesInstanceUID,
|
||||
value: ds.predecessorImageId || ds.SeriesInstanceUID,
|
||||
seriesNumber: isFinite(ds.SeriesNumber) ? ds.SeriesNumber : minSeriesNumber,
|
||||
description: ds.SeriesDescription,
|
||||
label: `${ds.SeriesDescription} ${ds.SeriesDate}/${ds.SeriesTime} ${ds.SeriesNumber}`,
|
||||
}));
|
||||
@@ -41,11 +57,12 @@ function ReportDialog({ dataSources, hide, onSave, onCancel }: ReportDialogProps
|
||||
{
|
||||
value: null,
|
||||
description: null,
|
||||
seriesNumber: minSeriesNumber,
|
||||
label: 'Create new series',
|
||||
},
|
||||
...options,
|
||||
];
|
||||
}, [displaySetService]);
|
||||
}, [displaySetService, modality]);
|
||||
|
||||
useEffect(() => {
|
||||
const seriesOption = seriesOptions.find(s => s.value === selectedSeries);
|
||||
@@ -58,6 +75,7 @@ function ReportDialog({ dataSources, hide, onSave, onCancel }: ReportDialogProps
|
||||
onSave({
|
||||
reportName,
|
||||
dataSource: selectedDataSource,
|
||||
priorSeriesNumber: Math.max(...seriesOptions.map(it => it.seriesNumber)),
|
||||
series: selectedSeries,
|
||||
});
|
||||
hide();
|
||||
|
||||
Loaded 100 of 335 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user