Compare commits
86
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7a69c46dc0 | ||
|
|
70367bad0b | ||
|
|
cd347812bf | ||
|
|
befd7d1b08 | ||
|
|
618b0fc516 | ||
|
|
693bfc5ac3 | ||
|
|
c14daf6735 | ||
|
|
5e35fbb21d | ||
|
|
efd02088f1 | ||
|
|
d7f2b3acaf | ||
|
|
c609acaaad | ||
|
|
4db092f4bf | ||
|
|
994abf2a6a | ||
|
|
c7db3fca24 | ||
|
|
ce8e00ea73 | ||
|
|
4d4356338c | ||
|
|
9d9e1aa4fe | ||
|
|
884224541b | ||
|
|
69a61698ae | ||
|
|
82b4f72408 | ||
|
|
c87d933bbc | ||
|
|
b1745c5157 | ||
|
|
096590a7e9 | ||
|
|
106b67d436 | ||
|
|
cefe3c3cfb | ||
|
|
2a2d5eefcb | ||
|
|
3f55e57056 | ||
|
|
30515b8ab5 | ||
|
|
24bbf2b190 | ||
|
|
cb9aa6a765 | ||
|
|
5d9e4c9b02 | ||
|
|
dab095b6b5 | ||
|
|
cfa697aabc | ||
|
|
3350b1faa6 | ||
|
|
4ffb50c7e9 | ||
|
|
1d29554a91 | ||
|
|
b9d484fb2e | ||
|
|
597ac11c4d | ||
|
|
6007f340bf | ||
|
|
44738078db | ||
|
|
686fe7d931 | ||
|
|
0b8facbea6 | ||
|
|
0f89aa32f9 | ||
|
|
545161d5e8 | ||
|
|
eaa423c64e | ||
|
|
f7db74a843 | ||
|
|
7aba01f534 | ||
|
|
c5ebeaa127 | ||
|
|
d7e1d2c975 | ||
|
|
eb7eb56d7e | ||
|
|
c5ee092bf2 | ||
|
|
e2f4f95222 | ||
|
|
4546ede9bb | ||
|
|
e0fb439e98 | ||
|
|
ebbc9e0d6d | ||
|
|
47a6130fd5 | ||
|
|
80dfdcc37c | ||
|
|
d74ad370e0 | ||
|
|
ddfde9279e | ||
|
|
aaab6d2622 | ||
|
|
d2921083e7 | ||
|
|
a6338ea266 | ||
|
|
fe9551b9b1 | ||
|
|
ef60156a47 | ||
|
|
850de2903a | ||
|
|
f26ed5539b | ||
|
|
21aeaa63c7 | ||
|
|
2ac0d3f017 | ||
|
|
efdbe8e7b4 | ||
|
|
67b70eaa98 | ||
|
|
5150ed9427 | ||
|
|
1bcb0acf9f | ||
|
|
5d6cf2a609 | ||
|
|
9f813e0ea0 | ||
|
|
49de44ba34 | ||
|
|
b04e186274 | ||
|
|
c59d66412f | ||
|
|
f8b4ccbb59 | ||
|
|
5426773ffc | ||
|
|
a507a00f59 | ||
|
|
cedeb756c0 | ||
|
|
64385485b6 | ||
|
|
32828d9a6e | ||
|
|
b94d4782d1 | ||
|
|
36b47ce3a3 | ||
|
|
fe57c00577 |
No files matched your search
@@ -2,23 +2,21 @@
|
|||||||
name: "\U0001F41B Bug report"
|
name: "\U0001F41B Bug report"
|
||||||
about: Create a report to help us improve
|
about: Create a report to help us improve
|
||||||
title: ''
|
title: ''
|
||||||
labels: 'Community: Report :bug:, Awaiting Reproduction, Triage :white_flag:'
|
labels: 'Community: Report :bug:, Awaiting Reproduction'
|
||||||
assignees: ''
|
assignees: ''
|
||||||
---
|
---
|
||||||
|
|
||||||
> **Before Creating an issue**
|
<!-- ⚠️⚠️ Do Not Delete This! bug_report_template ⚠️⚠️ -->
|
||||||
>
|
<!-- Please read our Rules of Conduct: https://github.com/OHIF/Viewers/blob/master/CODE_OF_CONDUCT.md -->
|
||||||
> - Are you running the latest version?
|
<!-- 🕮 Read our guide about our process: https://v3-docs.ohif.org/development/our-process -->
|
||||||
> - Are you reporting to the correct repository?
|
<!-- 🔎 Search existing issues to avoid creating duplicates. -->
|
||||||
> - Did you search existing issues?
|
<!-- 🧪 Test using the latest version -->
|
||||||
|
|
||||||
## Bug Report
|
|
||||||
|
|
||||||
### Describe the Bug
|
### Describe the Bug
|
||||||
|
|
||||||
_A clear and concise description of what the bug is._
|
_A clear and concise description of what the bug is._
|
||||||
|
|
||||||
### What steps can we follow to reproduce the bug?
|
### Steps to Reproduce:
|
||||||
|
|
||||||
1. First step
|
1. First step
|
||||||
2. Second step
|
2. Second step
|
||||||
@@ -28,6 +26,15 @@ _A clear and concise description of what the bug is._
|
|||||||
Please use code blocks to show formatted errors or code snippets
|
Please use code blocks to show formatted errors or code snippets
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### The current behavior
|
||||||
|
|
||||||
|
_A clear and concise description of what happens instead of the expected
|
||||||
|
behavior._
|
||||||
|
|
||||||
|
### The expected behavior
|
||||||
|
|
||||||
|
_A clear and concise description of what you expected to happen._
|
||||||
|
|
||||||
> :warning: Reports we cannot reproduce are at risk of being marked stale and
|
> :warning: Reports we cannot reproduce are at risk of being marked stale and
|
||||||
> closed. The more information you can provide, the more likely we are to look
|
> closed. The more information you can provide, the more likely we are to look
|
||||||
> into and address your issue.
|
> into and address your issue.
|
||||||
@@ -2,19 +2,28 @@
|
|||||||
name: "\U0001F680 Feature request"
|
name: "\U0001F680 Feature request"
|
||||||
about: Suggest an idea for this project
|
about: Suggest an idea for this project
|
||||||
title: ''
|
title: ''
|
||||||
labels: 'Community: Request :hand:, Triage :white_flag:'
|
labels: 'Community: Request :hand:'
|
||||||
assignees: ''
|
assignees: ''
|
||||||
---
|
---
|
||||||
|
|
||||||
> :hand: Many people requests features. Tell us why yours is important to the
|
<!-- ⚠️⚠️ Do Not Delete This! feature_request_template ⚠️⚠️ -->
|
||||||
|
<!-- Please read our Rules of Conduct: https://github.com/OHIF/Viewers/blob/master/CODE_OF_CONDUCT.md -->
|
||||||
|
|
||||||
|
<!-- :hand: Many people requests features. Tell us why yours is important to the
|
||||||
> community. How does it add value? Why _this feature_?
|
> community. How does it add value? Why _this feature_?
|
||||||
>
|
>
|
||||||
> Is your request very specific to your needs? Consider
|
> Is your request very specific to your needs? Consider
|
||||||
> [contributing it](https://docs.ohif.org/contributing.html) yourself! Or reach
|
> [contributing it](https://v3-docs.ohif.org/development/contributing) yourself!
|
||||||
> out to a community member that offers
|
> Or reach out to a community member that offers
|
||||||
> [consulting services](https://docs.ohif.org/help.html#paid--commercial).
|
> [consulting services](https://docs.ohif.org/help.html#paid--commercial).
|
||||||
|
-->
|
||||||
|
|
||||||
## Request
|
### Request
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Please include as much detail as possible including possibly mock up screen shots, workflow or logic flow diagrams etc.
|
||||||
|
Discuss if and how the requested feature interacts with existing features
|
||||||
|
-->
|
||||||
|
|
||||||
**What feature or change would you like to see made?**
|
**What feature or change would you like to see made?**
|
||||||
|
|
||||||
|
|||||||
@@ -1,14 +1,91 @@
|
|||||||
### PR Checklist
|
<!-- Do Not Delete This! pr_template -->
|
||||||
|
<!-- Please read our Rules of Conduct: https://github.com/OHIF/Viewers/blob/master/CODE_OF_CONDUCT.md -->
|
||||||
- [ ] Brief description of changes
|
<!-- 🕮 Read our guide about our Contributing Guide here https://v3-docs.ohif.org/development/contributing -->
|
||||||
- [ ] Links to any relevant issues
|
<!-- :hand: Thank you for starting this amazing contribution! -->
|
||||||
- [ ] Required status checks are passing
|
|
||||||
- [ ] User cases if changes impact the user's experience
|
|
||||||
- [ ] `@mention` a maintainer to request a review
|
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
Links
|
⚠️⚠️ Please make sure the checklist section below is complete before submitting your PR.
|
||||||
-->
|
To complete the checklist, add an 'x' to each item: [] -> [x]
|
||||||
|
(PRs that do not have all the checkboxes marked will not be approved)
|
||||||
|
-->
|
||||||
|
|
||||||
|
### Context
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Provide a clear explanation of the reasoning behind this change, such as:
|
||||||
|
- A link to the issue being addressed, using the format "Fixes #ISSUE_NUMBER"
|
||||||
|
- An image showing the issue or problem being addressed (if not already in the issue)
|
||||||
|
- Error logs or callStacks to help with the understanding of the problem (if not already in the issue)
|
||||||
|
-->
|
||||||
|
|
||||||
|
### Changes & Results
|
||||||
|
|
||||||
|
<!--
|
||||||
|
List all the changes that have been done, such as:
|
||||||
|
- Add new components
|
||||||
|
- Remove old components
|
||||||
|
- Update dependencies
|
||||||
|
|
||||||
|
What are the effects of this change?
|
||||||
|
- Before vs After
|
||||||
|
- Screenshots / GIFs / Videos
|
||||||
|
-->
|
||||||
|
|
||||||
|
### Testing
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Describe how we can test your changes.
|
||||||
|
- open a URL
|
||||||
|
- visit a page
|
||||||
|
- click on a button
|
||||||
|
- etc.
|
||||||
|
-->
|
||||||
|
|
||||||
|
### Checklist
|
||||||
|
|
||||||
|
#### PR
|
||||||
|
|
||||||
|
<!--
|
||||||
|
https://semantic-release.gitbook.io/semantic-release/#how-does-it-work
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
Please note the letter casing in the provided examples (upper or lower).
|
||||||
|
|
||||||
|
- feat(MeasurementService): add ...
|
||||||
|
- fix(Toolbar): fix ...
|
||||||
|
- docs(Readme): update ...
|
||||||
|
- style(Whitespace): fix ...
|
||||||
|
- refactor(ExtensionManager): ...
|
||||||
|
- test(HangingProtocol): Add test ...
|
||||||
|
- chore(git): update ...
|
||||||
|
- perf(VolumeLoader): ...
|
||||||
|
|
||||||
|
You don't need to have each commit within the Pull Request follow the rule,
|
||||||
|
but the PR title must comply with it, as it will be used as the commit message
|
||||||
|
after the commits are squashed.
|
||||||
|
-->
|
||||||
|
|
||||||
|
- [] My Pull Request title is descriptive, accurate and follows the
|
||||||
|
semantic-release format and guidelines.
|
||||||
|
|
||||||
|
#### Code
|
||||||
|
|
||||||
|
- [] My code has been well-documented (function documentation, inline comments,
|
||||||
|
etc.)
|
||||||
|
|
||||||
|
#### Public Documentation Updates
|
||||||
|
|
||||||
|
<!-- https://v3-docs.ohif.org/ -->
|
||||||
|
|
||||||
|
- [] The documentation page has been updated as necessary for any public API
|
||||||
|
additions or removals.
|
||||||
|
|
||||||
|
#### Tested Environment
|
||||||
|
|
||||||
|
- [] "OS: <!--[e.g. Windows 10, macOS 10.15.4]"-->
|
||||||
|
- [] "Node version: <!--[e.g. 16.14.0]"-->
|
||||||
|
- [] "Browser:
|
||||||
|
<!--[e.g. Chrome 83.0.4103.116, Firefox 77.0.1, Safari 13.1.1]"-->
|
||||||
|
|
||||||
<!-- prettier-ignore-start -->
|
<!-- prettier-ignore-start -->
|
||||||
[blog]: https://circleci.com/blog/triggering-trusted-ci-jobs-on-untrusted-forks/
|
[blog]: https://circleci.com/blog/triggering-trusted-ci-jobs-on-untrusted-forks/
|
||||||
|
|||||||
+3
-9
@@ -4,23 +4,17 @@
|
|||||||
# Number of days of inactivity before an issue becomes stale
|
# Number of days of inactivity before an issue becomes stale
|
||||||
daysUntilStale: 180
|
daysUntilStale: 180
|
||||||
# Number of days of inactivity before a stale issue is closed
|
# Number of days of inactivity before a stale issue is closed
|
||||||
daysUntilClose: 9000
|
daysUntilClose: 60
|
||||||
# Issues with these labels will never be considered stale
|
# Issues with these labels will never be considered stale
|
||||||
exemptLabels:
|
exemptLabels:
|
||||||
- 'Story :raised_hands:'
|
|
||||||
- 'Bug: Verified :bug:'
|
- 'Bug: Verified :bug:'
|
||||||
- 'Task: CI/Tooling :robot:'
|
|
||||||
- 'Task: Docs 📖'
|
|
||||||
- 'Task: Docs :book:'
|
|
||||||
- 'Task: Refactor :hammer_and_wrench:'
|
|
||||||
- 'Task: Tests :microscope:'
|
|
||||||
- 'PR: Awaiting Review 👀'
|
- 'PR: Awaiting Review 👀'
|
||||||
- 'Triage :white_flag:'
|
|
||||||
- 'Extension: Discussion'
|
|
||||||
- 'Announcement 🎉'
|
- 'Announcement 🎉'
|
||||||
- 'IDC:priority'
|
- 'IDC:priority'
|
||||||
- 'IDC:candidate'
|
- 'IDC:candidate'
|
||||||
- 'IDC:collaboration'
|
- 'IDC:collaboration'
|
||||||
|
- 'Community: Request :hand:'
|
||||||
|
- 'Community: Report :bug:'
|
||||||
# Label to use when marking an issue as stale
|
# Label to use when marking an issue as stale
|
||||||
staleLabel: 'Stale :baguette_bread:'
|
staleLabel: 'Stale :baguette_bread:'
|
||||||
# Comment to post when marking an issue as stale. Set to `false` to disable
|
# Comment to post when marking an issue as stale. Set to `false` to disable
|
||||||
|
|||||||
@@ -40,5 +40,10 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"gitbook-cli": "^2.3.2"
|
"gitbook-cli": "^2.3.2"
|
||||||
|
},
|
||||||
|
"resolutions": {
|
||||||
|
"underscore": "1.12.1",
|
||||||
|
"json-schema": "0.4.0",
|
||||||
|
"minimist": "1.2.8"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+31
-34
@@ -1481,10 +1481,10 @@ json-schema-traverse@^0.4.1:
|
|||||||
resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660"
|
resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660"
|
||||||
integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==
|
integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==
|
||||||
|
|
||||||
json-schema@0.2.3:
|
json-schema@0.2.3, json-schema@0.4.0:
|
||||||
version "0.2.3"
|
version "0.4.0"
|
||||||
resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13"
|
resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.4.0.tgz#f7de4cf6efab838ebaeb3236474cbba5a1930ab5"
|
||||||
integrity sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=
|
integrity sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==
|
||||||
|
|
||||||
json-stable-stringify@^1.0.1:
|
json-stable-stringify@^1.0.1:
|
||||||
version "1.0.1"
|
version "1.0.1"
|
||||||
@@ -1729,27 +1729,24 @@ mime-types@^2.1.11, mime-types@^2.1.12, mime-types@~2.1.19, mime-types@~2.1.7:
|
|||||||
dependencies:
|
dependencies:
|
||||||
mime-db "~1.38.0"
|
mime-db "~1.38.0"
|
||||||
|
|
||||||
minimatch@^3.0.0, minimatch@^3.0.2, minimatch@^3.0.4, minimatch@~3.0.3:
|
minimatch@^3.0.0, minimatch@^3.0.2, minimatch@^3.0.4:
|
||||||
version "3.0.4"
|
version "3.1.2"
|
||||||
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083"
|
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b"
|
||||||
integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==
|
integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==
|
||||||
dependencies:
|
dependencies:
|
||||||
brace-expansion "^1.1.7"
|
brace-expansion "^1.1.7"
|
||||||
|
|
||||||
minimist@0.0.8:
|
minimatch@~3.0.3:
|
||||||
version "0.0.8"
|
version "3.0.8"
|
||||||
resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d"
|
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.8.tgz#5e6a59bd11e2ab0de1cfb843eb2d82e546c321c1"
|
||||||
integrity sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=
|
integrity sha512-6FsRAQsxQ61mw+qP1ZzbL9Bc78x2p5OqNgNpnoAFLTrX8n5Kxph0CsnhmKKNXTWjXqU5L0pGPR7hYk+XWZr60Q==
|
||||||
|
dependencies:
|
||||||
|
brace-expansion "^1.1.7"
|
||||||
|
|
||||||
minimist@^1.2.0:
|
minimist@0.0.8, minimist@1.2.8, minimist@^1.2.0, minimist@~0.0.1:
|
||||||
version "1.2.0"
|
version "1.2.8"
|
||||||
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284"
|
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c"
|
||||||
integrity sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=
|
integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==
|
||||||
|
|
||||||
minimist@~0.0.1:
|
|
||||||
version "0.0.10"
|
|
||||||
resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.10.tgz#de3f98543dbf96082be48ad1a0c7cda836301dcf"
|
|
||||||
integrity sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8=
|
|
||||||
|
|
||||||
mississippi@^1.2.0, mississippi@^1.3.0, mississippi@~1.3.0:
|
mississippi@^1.2.0, mississippi@^1.3.0, mississippi@~1.3.0:
|
||||||
version "1.3.1"
|
version "1.3.1"
|
||||||
@@ -2491,19 +2488,19 @@ q@1.5.0:
|
|||||||
integrity sha1-3QG6ydBtMObyGa7LglPunr3DCPE=
|
integrity sha1-3QG6ydBtMObyGa7LglPunr3DCPE=
|
||||||
|
|
||||||
qs@~6.2.0:
|
qs@~6.2.0:
|
||||||
version "6.2.3"
|
version "6.2.4"
|
||||||
resolved "https://registry.yarnpkg.com/qs/-/qs-6.2.3.tgz#1cfcb25c10a9b2b483053ff39f5dfc9233908cfe"
|
resolved "https://registry.yarnpkg.com/qs/-/qs-6.2.4.tgz#d90821bb8537cecc140e6c34f54ec76e54b39b22"
|
||||||
integrity sha1-HPyyXBCpsrSDBT/zn138kjOQjP4=
|
integrity sha512-E57gmgKXqDda+qWTkUJgIwgJICK7zgMfqZZopTRKZ6mY9gzLlmJN9EpXNnDrTxXFlOM/a+I28kJkF/60rqgnYw==
|
||||||
|
|
||||||
qs@~6.4.0:
|
qs@~6.4.0:
|
||||||
version "6.4.0"
|
version "6.4.1"
|
||||||
resolved "https://registry.yarnpkg.com/qs/-/qs-6.4.0.tgz#13e26d28ad6b0ffaa91312cd3bf708ed351e7233"
|
resolved "https://registry.yarnpkg.com/qs/-/qs-6.4.1.tgz#2bad97710a5b661c366b378b1e3a44a592ff45e6"
|
||||||
integrity sha1-E+JtKK1rD/qpExLNO/cI7TUecjM=
|
integrity sha512-LQy1Q1fcva/UsnP/6Iaa4lVeM49WiOitu2T4hZCyA/elLKu37L99qcBJk4VCCk+rdLvnMzfKyiN3SZTqdAZGSQ==
|
||||||
|
|
||||||
qs@~6.5.2:
|
qs@~6.5.2:
|
||||||
version "6.5.2"
|
version "6.5.3"
|
||||||
resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36"
|
resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.3.tgz#3aeeffc91967ef6e35c0e488ef46fb296ab76aad"
|
||||||
integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==
|
integrity sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==
|
||||||
|
|
||||||
rc@^1.0.1, rc@^1.1.6:
|
rc@^1.0.1, rc@^1.1.6:
|
||||||
version "1.2.8"
|
version "1.2.8"
|
||||||
@@ -3175,10 +3172,10 @@ umask@~1.1.0:
|
|||||||
resolved "https://registry.yarnpkg.com/umask/-/umask-1.1.0.tgz#f29cebf01df517912bb58ff9c4e50fde8e33320d"
|
resolved "https://registry.yarnpkg.com/umask/-/umask-1.1.0.tgz#f29cebf01df517912bb58ff9c4e50fde8e33320d"
|
||||||
integrity sha1-8pzr8B31F5ErtY/5xOUP3o4zMg0=
|
integrity sha1-8pzr8B31F5ErtY/5xOUP3o4zMg0=
|
||||||
|
|
||||||
underscore@^1.7.0:
|
underscore@1.12.1, underscore@^1.7.0:
|
||||||
version "1.9.1"
|
version "1.12.1"
|
||||||
resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.9.1.tgz#06dce34a0e68a7babc29b365b8e74b8925203961"
|
resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.12.1.tgz#7bb8cc9b3d397e201cf8553336d262544ead829e"
|
||||||
integrity sha512-5/4etnCkd9c8gwgowi5/om/mYO5ajCaOgdzj/oW+0eQV9WxKBDZw5+ycmKmeaTXjInS/W0BzpGLo2xR2aBwZdg==
|
integrity sha512-hEQt0+ZLDVUMhebKxL4x1BTtDY7bavVofhZ9KZ4aI26X9SRaE+Y3m83XUL1UP2jn8ynjndwCCpEHdUG+9pP1Tw==
|
||||||
|
|
||||||
unique-filename@^1.1.0, unique-filename@~1.1.0:
|
unique-filename@^1.1.0, unique-filename@~1.1.0:
|
||||||
version "1.1.1"
|
version "1.1.1"
|
||||||
|
|||||||
@@ -3,6 +3,70 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
## [2.12.16](https://github.com/OHIF/Viewers/compare/@ohif/extension-cornerstone@2.12.15...@ohif/extension-cornerstone@2.12.16) (2022-12-21)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-cornerstone
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [2.12.15](https://github.com/OHIF/Viewers/compare/@ohif/extension-cornerstone@2.12.14...@ohif/extension-cornerstone@2.12.15) (2022-09-20)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-cornerstone
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [2.12.14](https://github.com/OHIF/Viewers/compare/@ohif/extension-cornerstone@2.12.13...@ohif/extension-cornerstone@2.12.14) (2022-09-01)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-cornerstone
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [2.12.13](https://github.com/OHIF/Viewers/compare/@ohif/extension-cornerstone@2.12.12...@ohif/extension-cornerstone@2.12.13) (2022-06-30)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-cornerstone
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [2.12.12](https://github.com/OHIF/Viewers/compare/@ohif/extension-cornerstone@2.12.11...@ohif/extension-cornerstone@2.12.12) (2022-06-29)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-cornerstone
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [2.12.11](https://github.com/OHIF/Viewers/compare/@ohif/extension-cornerstone@2.12.10...@ohif/extension-cornerstone@2.12.11) (2022-06-14)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-cornerstone
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [2.12.10](https://github.com/OHIF/Viewers/compare/@ohif/extension-cornerstone@2.12.9...@ohif/extension-cornerstone@2.12.10) (2022-04-05)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-cornerstone
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [2.12.9](https://github.com/OHIF/Viewers/compare/@ohif/extension-cornerstone@2.12.8...@ohif/extension-cornerstone@2.12.9) (2022-03-25)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-cornerstone
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## [2.12.8](https://github.com/OHIF/Viewers/compare/@ohif/extension-cornerstone@2.12.7...@ohif/extension-cornerstone@2.12.8) (2022-03-14)
|
## [2.12.8](https://github.com/OHIF/Viewers/compare/@ohif/extension-cornerstone@2.12.7...@ohif/extension-cornerstone@2.12.8) (2022-03-14)
|
||||||
|
|
||||||
**Note:** Version bump only for package @ohif/extension-cornerstone
|
**Note:** Version bump only for package @ohif/extension-cornerstone
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@ohif/extension-cornerstone",
|
"name": "@ohif/extension-cornerstone",
|
||||||
"version": "2.12.8",
|
"version": "2.12.16",
|
||||||
"description": "OHIF extension for Cornerstone",
|
"description": "OHIF extension for Cornerstone",
|
||||||
"author": "OHIF",
|
"author": "OHIF",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
@@ -35,8 +35,8 @@
|
|||||||
"cornerstone-core": "^2.6.1",
|
"cornerstone-core": "^2.6.1",
|
||||||
"cornerstone-math": "^0.1.9",
|
"cornerstone-math": "^0.1.9",
|
||||||
"cornerstone-tools": "^6.0.6",
|
"cornerstone-tools": "^6.0.6",
|
||||||
"cornerstone-wado-image-loader": "^4.1.0",
|
"cornerstone-wado-image-loader": "^4.2.1",
|
||||||
"dcmjs": "0.19.7",
|
"dcmjs": "0.29.3",
|
||||||
"dicom-parser": "^1.8.11",
|
"dicom-parser": "^1.8.11",
|
||||||
"hammerjs": "^2.0.8",
|
"hammerjs": "^2.0.8",
|
||||||
"prop-types": "^15.6.2",
|
"prop-types": "^15.6.2",
|
||||||
@@ -44,7 +44,8 @@
|
|||||||
"react-dom": "^16.8.6",
|
"react-dom": "^16.8.6",
|
||||||
"react-redux": "^6.0.0",
|
"react-redux": "^6.0.0",
|
||||||
"react-resize-detector": "^3.4.0",
|
"react-resize-detector": "^3.4.0",
|
||||||
"redux": "^4.0.1"
|
"redux": "^4.0.1",
|
||||||
|
"styled-components": "^5.3.5"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "^7.5.5",
|
"@babel/runtime": "^7.5.5",
|
||||||
|
|||||||
@@ -237,11 +237,21 @@ class OHIFCornerstoneViewport extends Component {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const warningsOverlay = props => {
|
const overlay = props => {
|
||||||
|
const displaySet = this.props.viewportData.displaySet;
|
||||||
|
let filteredSRLabels;
|
||||||
|
if (displaySet.SRLabels && displaySet.SRLabels.length !== 0) {
|
||||||
|
filteredSRLabels = displaySet.SRLabels.filter(
|
||||||
|
SRLabel =>
|
||||||
|
SRLabel.ReferencedSOPInstanceUID === displaySet.SOPInstanceUID
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<OHIFCornerstoneViewportOverlay
|
<OHIFCornerstoneViewportOverlay
|
||||||
{...props}
|
{...props}
|
||||||
inconsistencyWarnings={inconsistencyWarnings}
|
inconsistencyWarnings={inconsistencyWarnings}
|
||||||
|
SRLabels={filteredSRLabels}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -254,7 +264,7 @@ class OHIFCornerstoneViewport extends Component {
|
|||||||
imageIdIndex={currentImageIdIndex}
|
imageIdIndex={currentImageIdIndex}
|
||||||
onNewImageDebounced={newImageHandler}
|
onNewImageDebounced={newImageHandler}
|
||||||
onNewImageDebounceTime={300}
|
onNewImageDebounceTime={300}
|
||||||
viewportOverlayComponent={warningsOverlay}
|
viewportOverlayComponent={overlay}
|
||||||
stackPrefetch={this.props.stackPrefetch}
|
stackPrefetch={this.props.stackPrefetch}
|
||||||
isStackPrefetchEnabled={this.props.isStackPrefetchEnabled}
|
isStackPrefetchEnabled={this.props.isStackPrefetchEnabled}
|
||||||
// ~~ Connected (From REDUX)
|
// ~~ Connected (From REDUX)
|
||||||
|
|||||||
@@ -33,6 +33,10 @@
|
|||||||
bottom: 140px;
|
bottom: 140px;
|
||||||
left: 20px;
|
left: 20px;
|
||||||
}
|
}
|
||||||
|
.OHIFCornerstoneViewportOverlay .bottom-left3 {
|
||||||
|
bottom: 110px;
|
||||||
|
left: -20px;
|
||||||
|
}
|
||||||
.OHIFCornerstoneViewportOverlay .bottom-right {
|
.OHIFCornerstoneViewportOverlay .bottom-right {
|
||||||
bottom: 20px;
|
bottom: 20px;
|
||||||
right: 20px;
|
right: 20px;
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { PureComponent } from 'react';
|
import { PureComponent } from 'react';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import styled from 'styled-components';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import cornerstone from 'cornerstone-core';
|
import cornerstone from 'cornerstone-core';
|
||||||
import './OHIFCornerstoneViewportOverlay.css';
|
import './OHIFCornerstoneViewportOverlay.css';
|
||||||
@@ -9,13 +10,27 @@ import {
|
|||||||
formatDICOMDate,
|
formatDICOMDate,
|
||||||
formatDICOMTime,
|
formatDICOMTime,
|
||||||
formatPN,
|
formatPN,
|
||||||
getCompression
|
getCompression,
|
||||||
} from '../utils/formatStudy';
|
} from '../utils/formatStudy';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { Icon } from '@ohif/ui/src/elements/Icon';
|
import { Icon } from '@ohif/ui/src/elements/Icon';
|
||||||
import { Tooltip } from '@ohif/ui/src/components/tooltip';
|
import { Tooltip } from '@ohif/ui/src/components/tooltip';
|
||||||
import { OverlayTrigger } from '@ohif/ui/src/components/overlayTrigger';
|
import { OverlayTrigger } from '@ohif/ui/src/components/overlayTrigger';
|
||||||
|
|
||||||
|
const Button = styled.button`
|
||||||
|
color: white;
|
||||||
|
padding: 2px 7px;
|
||||||
|
border-radius: 10px;
|
||||||
|
outline: 0;
|
||||||
|
text-transform: none;
|
||||||
|
margin: 2px 2px;
|
||||||
|
cursor: pointer;
|
||||||
|
&:disabled {
|
||||||
|
cursor: default;
|
||||||
|
opacity: 0.9;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
class OHIFCornerstoneViewportOverlay extends PureComponent {
|
class OHIFCornerstoneViewportOverlay extends PureComponent {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
scale: PropTypes.number.isRequired,
|
scale: PropTypes.number.isRequired,
|
||||||
@@ -30,11 +45,19 @@ class OHIFCornerstoneViewportOverlay extends PureComponent {
|
|||||||
imageId: PropTypes.string.isRequired,
|
imageId: PropTypes.string.isRequired,
|
||||||
imageIndex: PropTypes.number.isRequired,
|
imageIndex: PropTypes.number.isRequired,
|
||||||
stackSize: PropTypes.number.isRequired,
|
stackSize: PropTypes.number.isRequired,
|
||||||
inconsistencyWarnings: PropTypes.array.isRequired
|
inconsistencyWarnings: PropTypes.array,
|
||||||
|
SRLabels: PropTypes.array,
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { imageId, scale, windowWidth, windowCenter, inconsistencyWarnings } = this.props;
|
const {
|
||||||
|
imageId,
|
||||||
|
scale,
|
||||||
|
windowWidth,
|
||||||
|
windowCenter,
|
||||||
|
inconsistencyWarnings,
|
||||||
|
SRLabels,
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
if (!imageId) {
|
if (!imageId) {
|
||||||
return null;
|
return null;
|
||||||
@@ -72,8 +95,11 @@ class OHIFCornerstoneViewportOverlay extends PureComponent {
|
|||||||
|
|
||||||
const { imageIndex, stackSize } = this.props;
|
const { imageIndex, stackSize } = this.props;
|
||||||
|
|
||||||
const inconsistencyWarningsOn = inconsistencyWarnings && inconsistencyWarnings.length !== 0 ? true : false;
|
const inconsistencyWarningsOn =
|
||||||
const getWarningContent = (warningList) => {
|
inconsistencyWarnings && inconsistencyWarnings.length !== 0
|
||||||
|
? true
|
||||||
|
: false;
|
||||||
|
const getWarningContent = warningList => {
|
||||||
if (Array.isArray(warningList)) {
|
if (Array.isArray(warningList)) {
|
||||||
const listedWarnings = warningList.map((warn, index) => {
|
const listedWarnings = warningList.map((warn, index) => {
|
||||||
return <li key={index}>{warn}</li>;
|
return <li key={index}>{warn}</li>;
|
||||||
@@ -86,33 +112,100 @@ class OHIFCornerstoneViewportOverlay extends PureComponent {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const getWarningInfo = (seriesNumber, inconsistencyWarnings) => {
|
const getWarningInfo = (seriesNumber, inconsistencyWarnings) => {
|
||||||
return(
|
return (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
{inconsistencyWarnings.length != 0 ? (
|
{inconsistencyWarnings.length != 0 ? (
|
||||||
<OverlayTrigger
|
<OverlayTrigger
|
||||||
key={seriesNumber}
|
key={seriesNumber}
|
||||||
placement="left"
|
placement="left"
|
||||||
overlay={
|
overlay={
|
||||||
<Tooltip
|
<Tooltip
|
||||||
placement="left"
|
placement="left"
|
||||||
className="in tooltip-warning"
|
className="in tooltip-warning"
|
||||||
id="tooltip-left"
|
id="tooltip-left"
|
||||||
>
|
>
|
||||||
<div className="warningTitle">Series Inconsistencies</div>
|
<div className="warningTitle">Series Inconsistencies</div>
|
||||||
<div className="warningContent">{getWarningContent(inconsistencyWarnings)}</div>
|
<div className="warningContent">
|
||||||
</Tooltip>
|
{getWarningContent(inconsistencyWarnings)}
|
||||||
}
|
</div>
|
||||||
>
|
</Tooltip>
|
||||||
<div className={classNames('warning')}>
|
}
|
||||||
<span className="warning-icon">
|
>
|
||||||
<Icon name="exclamation-triangle" />
|
<div className={classNames('warning')}>
|
||||||
</span>
|
<span className="warning-icon">
|
||||||
</div>
|
<Icon name="exclamation-triangle" />
|
||||||
</OverlayTrigger>
|
</span>
|
||||||
) : (
|
</div>
|
||||||
<React.Fragment></React.Fragment>
|
</OverlayTrigger>
|
||||||
|
) : (
|
||||||
|
<React.Fragment></React.Fragment>
|
||||||
)}
|
)}
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const SRLabelsOn = SRLabels && SRLabels.length !== 0 ? true : false;
|
||||||
|
|
||||||
|
/**/
|
||||||
|
|
||||||
|
const getSRLabelsContent = SRLabels => {
|
||||||
|
if (Array.isArray(SRLabels)) {
|
||||||
|
const listedSRLabels = SRLabels.map((SRLabel, index) => {
|
||||||
|
const color = SRLabel.labels.color;
|
||||||
|
return (
|
||||||
|
SRLabel.labels.visible && (
|
||||||
|
<OverlayTrigger
|
||||||
|
key={index}
|
||||||
|
placement="top"
|
||||||
|
overlay={
|
||||||
|
<Tooltip
|
||||||
|
placement="top"
|
||||||
|
className="in tooltip-warning"
|
||||||
|
id="tooltip-top"
|
||||||
|
>
|
||||||
|
<div className="warningTitle">
|
||||||
|
{' '}
|
||||||
|
Coding scheme designators{' '}
|
||||||
|
</div>
|
||||||
|
<div className="warningContent">
|
||||||
|
{SRLabel.labels.labelCodingSchemeDesignator +
|
||||||
|
' : ' +
|
||||||
|
SRLabel.labels.valueCodingSchemeDesignator}
|
||||||
|
</div>
|
||||||
|
</Tooltip>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<div style={{ display: 'inline-block' }}>
|
||||||
|
<Button
|
||||||
|
style={{
|
||||||
|
backgroundColor: color,
|
||||||
|
}}
|
||||||
|
disabled={true}
|
||||||
|
key={index}
|
||||||
|
>
|
||||||
|
{SRLabel.labels.label + ' : ' + SRLabel.labels.value}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</OverlayTrigger>
|
||||||
|
)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
return <ol>{listedSRLabels}</ol>;
|
||||||
|
} else {
|
||||||
|
return <React.Fragment></React.Fragment>;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const getSRLabelsInfo = SRLabels => {
|
||||||
|
return (
|
||||||
|
<React.Fragment>
|
||||||
|
{SRLabels.length != 0 ? (
|
||||||
|
getSRLabelsContent(SRLabels)
|
||||||
|
) : (
|
||||||
|
<React.Fragment></React.Fragment>
|
||||||
|
)}
|
||||||
|
</React.Fragment>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -134,7 +227,14 @@ class OHIFCornerstoneViewportOverlay extends PureComponent {
|
|||||||
<div className="compressionIndicator">{compression}</div>
|
<div className="compressionIndicator">{compression}</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="bottom-left2 warning">
|
<div className="bottom-left2 warning">
|
||||||
<div>{inconsistencyWarningsOn ? getWarningInfo(seriesNumber, inconsistencyWarnings) : ''}</div>
|
<div>
|
||||||
|
{inconsistencyWarningsOn
|
||||||
|
? getWarningInfo(seriesNumber, inconsistencyWarnings)
|
||||||
|
: ''}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="bottom-left3 warning">
|
||||||
|
<div>{SRLabelsOn ? getSRLabelsInfo(SRLabels) : ''}</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="bottom-left overlay-element">
|
<div className="bottom-left overlay-element">
|
||||||
<div>{seriesNumber >= 0 ? `Ser: ${seriesNumber}` : ''}</div>
|
<div>{seriesNumber >= 0 ? `Ser: ${seriesNumber}` : ''}</div>
|
||||||
|
|||||||
@@ -97,6 +97,8 @@ export default class DICOMSRDisplayTool extends BaseTool {
|
|||||||
const renderableDataForGraphicType = renderableData[GraphicType];
|
const renderableDataForGraphicType = renderableData[GraphicType];
|
||||||
|
|
||||||
switch (GraphicType) {
|
switch (GraphicType) {
|
||||||
|
case SCOORD_TYPES.TEXT:
|
||||||
|
break;
|
||||||
case SCOORD_TYPES.POINT:
|
case SCOORD_TYPES.POINT:
|
||||||
this.renderPoint(renderableDataForGraphicType, eventData, options);
|
this.renderPoint(renderableDataForGraphicType, eventData, options);
|
||||||
break;
|
break;
|
||||||
@@ -167,20 +169,22 @@ export default class DICOMSRDisplayTool extends BaseTool {
|
|||||||
return data.handles.textBox.anchorPoints;
|
return data.handles.textBox.anchorPoints;
|
||||||
}
|
}
|
||||||
|
|
||||||
draw(context, context => {
|
if (data.handles.textBox.anchorPoints.length !== 0) {
|
||||||
drawLinkedTextBox(
|
draw(context, context => {
|
||||||
context,
|
drawLinkedTextBox(
|
||||||
element,
|
context,
|
||||||
data.handles.textBox,
|
element,
|
||||||
text,
|
data.handles.textBox,
|
||||||
data.handles,
|
text,
|
||||||
textBoxAnchorPoints,
|
data.handles,
|
||||||
color,
|
textBoxAnchorPoints,
|
||||||
lineWidth,
|
color,
|
||||||
0,
|
lineWidth,
|
||||||
true
|
0,
|
||||||
);
|
true
|
||||||
});
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TOOD -> text boxes may overlap with other annotations at the moment.
|
// TOOD -> text boxes may overlap with other annotations at the moment.
|
||||||
@@ -230,7 +234,6 @@ export default class DICOMSRDisplayTool extends BaseTool {
|
|||||||
const { element, image } = eventData;
|
const { element, image } = eventData;
|
||||||
const { rows, columns } = image;
|
const { rows, columns } = image;
|
||||||
const context = getNewContext(eventData.canvasContext.canvas);
|
const context = getNewContext(eventData.canvasContext.canvas);
|
||||||
|
|
||||||
const { color, lineWidth } = options;
|
const { color, lineWidth } = options;
|
||||||
|
|
||||||
// Find a suitable length for the image size.
|
// Find a suitable length for the image size.
|
||||||
@@ -332,6 +335,8 @@ function _getTextBoxAnchorPointsForRenderableData(renderableData, eventData) {
|
|||||||
const renderableDataForGraphicType = renderableData[GraphicType];
|
const renderableDataForGraphicType = renderableData[GraphicType];
|
||||||
|
|
||||||
switch (GraphicType) {
|
switch (GraphicType) {
|
||||||
|
case SCOORD_TYPES.TEXT:
|
||||||
|
break;
|
||||||
case SCOORD_TYPES.POINT:
|
case SCOORD_TYPES.POINT:
|
||||||
renderableDataForGraphicType.forEach(points => {
|
renderableDataForGraphicType.forEach(points => {
|
||||||
anchorPoints = [...anchorPoints, ...points];
|
anchorPoints = [...anchorPoints, ...points];
|
||||||
@@ -405,7 +410,7 @@ function _getBoundingBoxFromAnchorPoints(anchorPoints) {
|
|||||||
minX = x;
|
minX = x;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (y > maxX) {
|
if (y > maxY) {
|
||||||
maxY = y;
|
maxY = y;
|
||||||
} else if (y < minY) {
|
} else if (y < minY) {
|
||||||
minY = y;
|
minY = y;
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
export default {
|
export default {
|
||||||
|
TEXT: 'TEXT',
|
||||||
POINT: 'POINT',
|
POINT: 'POINT',
|
||||||
MULTIPOINT: 'MULTIPOINT',
|
MULTIPOINT: 'MULTIPOINT',
|
||||||
POLYLINE: 'POLYLINE',
|
POLYLINE: 'POLYLINE',
|
||||||
|
|||||||
@@ -3,6 +3,14 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
## [0.3.3](https://github.com/OHIF/Viewers/compare/@ohif/extension-debugging@0.3.2...@ohif/extension-debugging@0.3.3) (2022-06-30)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-debugging
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## [0.3.2](https://github.com/OHIF/Viewers/compare/@ohif/extension-debugging@0.3.1...@ohif/extension-debugging@0.3.2) (2022-03-10)
|
## [0.3.2](https://github.com/OHIF/Viewers/compare/@ohif/extension-debugging@0.3.1...@ohif/extension-debugging@0.3.2) (2022-03-10)
|
||||||
|
|
||||||
**Note:** Version bump only for package @ohif/extension-debugging
|
**Note:** Version bump only for package @ohif/extension-debugging
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@ohif/extension-debugging",
|
"name": "@ohif/extension-debugging",
|
||||||
"version": "0.3.2",
|
"version": "0.3.3",
|
||||||
"description": "OHIF extension for debugging.",
|
"description": "OHIF extension for debugging.",
|
||||||
"author": "OHIF",
|
"author": "OHIF",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
|||||||
@@ -89,10 +89,10 @@ const DubugReportModal = ({
|
|||||||
|
|
||||||
Object.keys(viewportSpecificData).forEach(viewportIndex => {
|
Object.keys(viewportSpecificData).forEach(viewportIndex => {
|
||||||
const displaySet = viewportSpecificData[viewportIndex];
|
const displaySet = viewportSpecificData[viewportIndex];
|
||||||
const [
|
const [referencedDisplaySetsRef] = _getReferencedSeriesInstanceUIDsString(
|
||||||
referencedDisplaySetsRef,
|
displaySet,
|
||||||
referencedDisplaySetsCount,
|
'SEG'
|
||||||
] = _getReferencedSeriesInstanceUIDsString(displaySet, 'SEG');
|
);
|
||||||
|
|
||||||
const [row, column] = _viewportIndexToViewportPosition(
|
const [row, column] = _viewportIndexToViewportPosition(
|
||||||
viewportIndex,
|
viewportIndex,
|
||||||
@@ -106,10 +106,27 @@ const DubugReportModal = ({
|
|||||||
|
|
||||||
Object.keys(viewportSpecificData).forEach(viewportIndex => {
|
Object.keys(viewportSpecificData).forEach(viewportIndex => {
|
||||||
const displaySet = viewportSpecificData[viewportIndex];
|
const displaySet = viewportSpecificData[viewportIndex];
|
||||||
const [
|
const [referencedDisplaySetsRef] = _getReferencedSeriesInstanceUIDsString(
|
||||||
referencedDisplaySetsRef,
|
displaySet,
|
||||||
referencedDisplaySetsCount,
|
'RTSTRUCT'
|
||||||
] = _getReferencedSeriesInstanceUIDsString(displaySet, 'RTSTRUCT');
|
);
|
||||||
|
|
||||||
|
const [row, column] = _viewportIndexToViewportPosition(
|
||||||
|
viewportIndex,
|
||||||
|
numColumns
|
||||||
|
);
|
||||||
|
|
||||||
|
body += `[${row},${column}]\t${referencedDisplaySetsRef}\n`;
|
||||||
|
});
|
||||||
|
|
||||||
|
body += '== ReferencedSRSeriesInstanceUIDs ==\n';
|
||||||
|
|
||||||
|
Object.keys(viewportSpecificData).forEach(viewportIndex => {
|
||||||
|
const displaySet = viewportSpecificData[viewportIndex];
|
||||||
|
const [referencedDisplaySetsRef] = _getReferencedSeriesInstanceUIDsString(
|
||||||
|
displaySet,
|
||||||
|
'SR'
|
||||||
|
);
|
||||||
|
|
||||||
const [row, column] = _viewportIndexToViewportPosition(
|
const [row, column] = _viewportIndexToViewportPosition(
|
||||||
viewportIndex,
|
viewportIndex,
|
||||||
@@ -261,11 +278,15 @@ const getLayout = viewports => {
|
|||||||
<tr>
|
<tr>
|
||||||
<th>ReferencedSEGSeriesInstanceUIDs</th>
|
<th>ReferencedSEGSeriesInstanceUIDs</th>
|
||||||
</tr>
|
</tr>
|
||||||
{getReferencedSEGSeriesInstanceUIDsPerRow(viewports)}
|
{getReferencedSeriesInstanceUIDsPerRow(viewports, 'SEG')}
|
||||||
<tr>
|
<tr>
|
||||||
<th>ReferencedRTSTRUCTSeriesInstanceUIDs</th>
|
<th>ReferencedRTSTRUCTSeriesInstanceUIDs</th>
|
||||||
</tr>
|
</tr>
|
||||||
{getReferencedRTSTRUCTSeriesInstanceUIDsPerRow(viewports)}
|
{getReferencedSeriesInstanceUIDsPerRow(viewports, 'RTSTRUCT')}
|
||||||
|
<tr>
|
||||||
|
<th>ReferencedSRSeriesInstanceUIDs</th>
|
||||||
|
</tr>
|
||||||
|
{getReferencedSeriesInstanceUIDsPerRow(viewports, 'SR')}
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -351,7 +372,7 @@ const _getReferencedSeriesInstanceUIDsString = (displaySet, Modality) => {
|
|||||||
return [referencedDisplaySetsRef, referencedDisplaySetsCount];
|
return [referencedDisplaySetsRef, referencedDisplaySetsCount];
|
||||||
};
|
};
|
||||||
|
|
||||||
const getReferencedSEGSeriesInstanceUIDsPerRow = viewports => {
|
const getReferencedSeriesInstanceUIDsPerRow = (viewports, Modality) => {
|
||||||
const { viewportSpecificData, numColumns } = viewports;
|
const { viewportSpecificData, numColumns } = viewports;
|
||||||
|
|
||||||
// NOTE viewportSpecificData is actually an object with numerical keys.
|
// NOTE viewportSpecificData is actually an object with numerical keys.
|
||||||
@@ -360,43 +381,7 @@ const getReferencedSEGSeriesInstanceUIDsPerRow = viewports => {
|
|||||||
const [
|
const [
|
||||||
referencedDisplaySetsRef,
|
referencedDisplaySetsRef,
|
||||||
referencedDisplaySetsCount,
|
referencedDisplaySetsCount,
|
||||||
] = _getReferencedSeriesInstanceUIDsString(displaySet, 'SEG');
|
] = _getReferencedSeriesInstanceUIDsString(displaySet, Modality);
|
||||||
|
|
||||||
const [row, column] = _viewportIndexToViewportPosition(
|
|
||||||
viewportIndex,
|
|
||||||
numColumns
|
|
||||||
);
|
|
||||||
|
|
||||||
if (referencedDisplaySetsCount > 1) {
|
|
||||||
return (
|
|
||||||
<tr>
|
|
||||||
<td>{`[${row},${column}]`}</td>
|
|
||||||
<td className="debug-overflowText-border">
|
|
||||||
{referencedDisplaySetsRef}
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
return (
|
|
||||||
<tr>
|
|
||||||
<td>{`[${row},${column}]`}</td>
|
|
||||||
<td className="debug-overflowText">{referencedDisplaySetsRef}</td>
|
|
||||||
</tr>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const getReferencedRTSTRUCTSeriesInstanceUIDsPerRow = viewports => {
|
|
||||||
const { viewportSpecificData, numColumns } = viewports;
|
|
||||||
|
|
||||||
// NOTE viewportSpecificData is actually an object with numerical keys.
|
|
||||||
return Object.keys(viewportSpecificData).map(viewportIndex => {
|
|
||||||
const displaySet = viewportSpecificData[viewportIndex];
|
|
||||||
const [
|
|
||||||
referencedDisplaySetsRef,
|
|
||||||
referencedDisplaySetsCount,
|
|
||||||
] = _getReferencedSeriesInstanceUIDsString(displaySet, 'RTSTRUCT');
|
|
||||||
|
|
||||||
const [row, column] = _viewportIndexToViewportPosition(
|
const [row, column] = _viewportIndexToViewportPosition(
|
||||||
viewportIndex,
|
viewportIndex,
|
||||||
|
|||||||
@@ -3,6 +3,46 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
## [1.3.24](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-html@1.3.23...@ohif/extension-dicom-html@1.3.24) (2022-12-21)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-dicom-html
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [1.3.23](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-html@1.3.22...@ohif/extension-dicom-html@1.3.23) (2022-09-01)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-dicom-html
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [1.3.22](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-html@1.3.21...@ohif/extension-dicom-html@1.3.22) (2022-06-30)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-dicom-html
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [1.3.21](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-html@1.3.20...@ohif/extension-dicom-html@1.3.21) (2022-04-05)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-dicom-html
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [1.3.20](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-html@1.3.19...@ohif/extension-dicom-html@1.3.20) (2022-03-25)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-dicom-html
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## [1.3.19](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-html@1.3.18...@ohif/extension-dicom-html@1.3.19) (2022-03-14)
|
## [1.3.19](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-html@1.3.18...@ohif/extension-dicom-html@1.3.19) (2022-03-14)
|
||||||
|
|
||||||
**Note:** Version bump only for package @ohif/extension-dicom-html
|
**Note:** Version bump only for package @ohif/extension-dicom-html
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@ohif/extension-dicom-html",
|
"name": "@ohif/extension-dicom-html",
|
||||||
"version": "1.3.19",
|
"version": "1.3.24",
|
||||||
"description": "OHIF extension for rendering structured reports to HTML",
|
"description": "OHIF extension for rendering structured reports to HTML",
|
||||||
"author": "OHIF",
|
"author": "OHIF",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
@@ -29,7 +29,7 @@
|
|||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@ohif/core": "^0.50.0",
|
"@ohif/core": "^0.50.0",
|
||||||
"dcmjs": "0.19.7",
|
"dcmjs": "0.29.3",
|
||||||
"prop-types": "^15.6.2",
|
"prop-types": "^15.6.2",
|
||||||
"react": "^16.8.6",
|
"react": "^16.8.6",
|
||||||
"react-dom": "^16.8.6"
|
"react-dom": "^16.8.6"
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { MODULE_TYPES, utils } from '@ohif/core';
|
import { MODULE_TYPES, utils, metadata } from '@ohif/core';
|
||||||
|
|
||||||
// TODO: Should probably use dcmjs for this
|
// TODO: Should probably use dcmjs for this
|
||||||
const SOP_CLASS_UIDS = {
|
const SOP_CLASS_UIDS = {
|
||||||
@@ -10,7 +10,7 @@ const SOP_CLASS_UIDS = {
|
|||||||
MAMMOGRAPHY_CAD_SR: '1.2.840.10008.5.1.4.1.1.88.50',
|
MAMMOGRAPHY_CAD_SR: '1.2.840.10008.5.1.4.1.1.88.50',
|
||||||
CHEST_CAD_SR: '1.2.840.10008.5.1.4.1.1.88.65',
|
CHEST_CAD_SR: '1.2.840.10008.5.1.4.1.1.88.65',
|
||||||
X_RAY_RADIATION_DOSE_SR: '1.2.840.10008.5.1.4.1.1.88.67',
|
X_RAY_RADIATION_DOSE_SR: '1.2.840.10008.5.1.4.1.1.88.67',
|
||||||
ACQUISITION_CONTEXT_SR_STORAGE: '1.2.840.10008.5.1.4.1.1.88.71'
|
ACQUISITION_CONTEXT_SR_STORAGE: '1.2.840.10008.5.1.4.1.1.88.71',
|
||||||
};
|
};
|
||||||
|
|
||||||
const sopClassUIDs = Object.values(SOP_CLASS_UIDS);
|
const sopClassUIDs = Object.values(SOP_CLASS_UIDS);
|
||||||
@@ -32,7 +32,7 @@ const OHIFDicomHtmlSopClassHandler = {
|
|||||||
SeriesTime,
|
SeriesTime,
|
||||||
} = metadata;
|
} = metadata;
|
||||||
|
|
||||||
return {
|
const srDisplaySet = {
|
||||||
plugin: 'html',
|
plugin: 'html',
|
||||||
Modality: 'SR',
|
Modality: 'SR',
|
||||||
displaySetInstanceUID: utils.guid(),
|
displaySetInstanceUID: utils.guid(),
|
||||||
@@ -49,7 +49,15 @@ const OHIFDicomHtmlSopClassHandler = {
|
|||||||
authorizationHeaders,
|
authorizationHeaders,
|
||||||
sopClassUids: sopClassUIDs,
|
sopClassUids: sopClassUIDs,
|
||||||
images: series._instances,
|
images: series._instances,
|
||||||
|
isDerived: true,
|
||||||
|
referencedDisplaySets: [],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
srDisplaySet.getSourceDisplaySet = function() {
|
||||||
|
return srDisplaySet.referencedDisplaySets;
|
||||||
|
};
|
||||||
|
|
||||||
|
return srDisplaySet;
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,46 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
## [0.7.14](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-rt@0.7.13...@ohif/extension-dicom-rt@0.7.14) (2022-12-21)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-dicom-rt
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [0.7.13](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-rt@0.7.12...@ohif/extension-dicom-rt@0.7.13) (2022-09-01)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-dicom-rt
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [0.7.12](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-rt@0.7.11...@ohif/extension-dicom-rt@0.7.12) (2022-08-31)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-dicom-rt
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [0.7.11](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-rt@0.7.10...@ohif/extension-dicom-rt@0.7.11) (2022-04-05)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-dicom-rt
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [0.7.10](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-rt@0.7.9...@ohif/extension-dicom-rt@0.7.10) (2022-03-25)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-dicom-rt
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## [0.7.9](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-rt@0.7.8...@ohif/extension-dicom-rt@0.7.9) (2022-03-14)
|
## [0.7.9](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-rt@0.7.8...@ohif/extension-dicom-rt@0.7.9) (2022-03-14)
|
||||||
|
|
||||||
**Note:** Version bump only for package @ohif/extension-dicom-rt
|
**Note:** Version bump only for package @ohif/extension-dicom-rt
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@ohif/extension-dicom-rt",
|
"name": "@ohif/extension-dicom-rt",
|
||||||
"version": "0.7.9",
|
"version": "0.7.14",
|
||||||
"description": "OHIF extension for rendering DICOM RTSTRUCTs on top of cornerstone images.",
|
"description": "OHIF extension for rendering DICOM RTSTRUCTs on top of cornerstone images.",
|
||||||
"author": "OHIF",
|
"author": "OHIF",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
@@ -31,7 +31,7 @@
|
|||||||
"@ohif/core": "^0.50.0",
|
"@ohif/core": "^0.50.0",
|
||||||
"cornerstone-core": "^2.6.1",
|
"cornerstone-core": "^2.6.1",
|
||||||
"cornerstone-tools": "^6.0.6",
|
"cornerstone-tools": "^6.0.6",
|
||||||
"dcmjs": "0.19.7",
|
"dcmjs": "0.29.3",
|
||||||
"gl-matrix": "^3.3.0",
|
"gl-matrix": "^3.3.0",
|
||||||
"prop-types": "^15.6.2",
|
"prop-types": "^15.6.2",
|
||||||
"react": "^16.8.6",
|
"react": "^16.8.6",
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import React, { useState, useEffect, useCallback } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import cornerstoneTools from 'cornerstone-tools';
|
import cornerstoneTools from 'cornerstone-tools';
|
||||||
import cornerstone from 'cornerstone-core';
|
import cornerstone from 'cornerstone-core';
|
||||||
|
|||||||
@@ -3,6 +3,108 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
## [0.7.21](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-segmentation@0.7.20...@ohif/extension-dicom-segmentation@0.7.21) (2023-01-17)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-dicom-segmentation
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [0.7.20](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-segmentation@0.7.19...@ohif/extension-dicom-segmentation@0.7.20) (2023-01-10)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-dicom-segmentation
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [0.7.19](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-segmentation@0.7.18...@ohif/extension-dicom-segmentation@0.7.19) (2022-12-21)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-dicom-segmentation
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [0.7.18](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-segmentation@0.7.17...@ohif/extension-dicom-segmentation@0.7.18) (2022-12-21)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-dicom-segmentation
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [0.7.17](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-segmentation@0.7.16...@ohif/extension-dicom-segmentation@0.7.17) (2022-12-12)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* [#2964](https://github.com/OHIF/Viewers/issues/2964) Reword message for segmentation error loading due to orientation tolerance ([#3017](https://github.com/OHIF/Viewers/issues/3017)) ([597ac11](https://github.com/OHIF/Viewers/commit/597ac11c4daee1b1e14148804551e20611cfef08))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [0.7.16](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-segmentation@0.7.15...@ohif/extension-dicom-segmentation@0.7.16) (2022-10-28)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* [#2964](https://github.com/OHIF/Viewers/issues/2964) Update message for segmentation error loading due to orientation tolerance ([#2982](https://github.com/OHIF/Viewers/issues/2982)) ([545161d](https://github.com/OHIF/Viewers/commit/545161d5e8d92bb6652f17dc24a36dac8aa7b3a4))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [0.7.15](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-segmentation@0.7.14...@ohif/extension-dicom-segmentation@0.7.15) (2022-09-19)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-dicom-segmentation
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [0.7.14](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-segmentation@0.7.13...@ohif/extension-dicom-segmentation@0.7.14) (2022-09-12)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-dicom-segmentation
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [0.7.13](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-segmentation@0.7.12...@ohif/extension-dicom-segmentation@0.7.13) (2022-09-01)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-dicom-segmentation
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [0.7.12](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-segmentation@0.7.11...@ohif/extension-dicom-segmentation@0.7.12) (2022-08-31)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-dicom-segmentation
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [0.7.11](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-segmentation@0.7.10...@ohif/extension-dicom-segmentation@0.7.11) (2022-04-05)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-dicom-segmentation
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [0.7.10](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-segmentation@0.7.9...@ohif/extension-dicom-segmentation@0.7.10) (2022-03-25)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-dicom-segmentation
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## [0.7.9](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-segmentation@0.7.8...@ohif/extension-dicom-segmentation@0.7.9) (2022-03-14)
|
## [0.7.9](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-segmentation@0.7.8...@ohif/extension-dicom-segmentation@0.7.9) (2022-03-14)
|
||||||
|
|
||||||
**Note:** Version bump only for package @ohif/extension-dicom-segmentation
|
**Note:** Version bump only for package @ohif/extension-dicom-segmentation
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@ohif/extension-dicom-segmentation",
|
"name": "@ohif/extension-dicom-segmentation",
|
||||||
"version": "0.7.9",
|
"version": "0.7.21",
|
||||||
"description": "OHIF extension for viewing segmentations in the 2D MPR view",
|
"description": "OHIF extension for viewing segmentations in the 2D MPR view",
|
||||||
"author": "OHIF",
|
"author": "OHIF",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
@@ -31,7 +31,7 @@
|
|||||||
"@ohif/core": "^0.50.0",
|
"@ohif/core": "^0.50.0",
|
||||||
"cornerstone-core": "^2.6.1",
|
"cornerstone-core": "^2.6.1",
|
||||||
"cornerstone-tools": "^6.0.6",
|
"cornerstone-tools": "^6.0.6",
|
||||||
"dcmjs": "0.19.7",
|
"dcmjs": "0.29.3",
|
||||||
"prop-types": "^15.6.2",
|
"prop-types": "^15.6.2",
|
||||||
"react": "^16.8.6",
|
"react": "^16.8.6",
|
||||||
"react-dom": "^16.8.6"
|
"react-dom": "^16.8.6"
|
||||||
|
|||||||
@@ -0,0 +1,100 @@
|
|||||||
|
import csTools from 'cornerstone-tools';
|
||||||
|
import cs from 'cornerstone-core';
|
||||||
|
import OHIF from '@ohif/core';
|
||||||
|
|
||||||
|
import DICOMSegTempCrosshairsTool from './tools/DICOMSegTempCrosshairsTool';
|
||||||
|
import refreshViewports from './utils/refreshViewports';
|
||||||
|
|
||||||
|
const { studyMetadataManager } = OHIF.utils;
|
||||||
|
|
||||||
|
const commandsModule = ({ commandsManager }) => {
|
||||||
|
const actions = {
|
||||||
|
jumpToFirstSegment: ({ viewports }) => {
|
||||||
|
try {
|
||||||
|
const { activeViewportIndex, viewportSpecificData } = viewports;
|
||||||
|
const viewport = viewportSpecificData[activeViewportIndex];
|
||||||
|
const { StudyInstanceUID, displaySetInstanceUID } = viewport;
|
||||||
|
const studyMetadata = studyMetadataManager.get(StudyInstanceUID);
|
||||||
|
const firstImageId = studyMetadata.getFirstImageId(
|
||||||
|
displaySetInstanceUID
|
||||||
|
);
|
||||||
|
|
||||||
|
const module = csTools.getModule('segmentation');
|
||||||
|
const brushStackState = module.state.series[firstImageId];
|
||||||
|
const { labelmaps3D, activeLabelmapIndex } = brushStackState;
|
||||||
|
const { labelmaps2D } = labelmaps3D[activeLabelmapIndex];
|
||||||
|
|
||||||
|
const firstLabelMap2D = labelmaps2D.find(value => !!value);
|
||||||
|
const firstSegment = firstLabelMap2D.segmentsOnLabelmap[0];
|
||||||
|
const segmentNumber = firstSegment;
|
||||||
|
|
||||||
|
const validIndexList = [];
|
||||||
|
labelmaps2D.forEach((labelMap2D, index) => {
|
||||||
|
if (labelMap2D.segmentsOnLabelmap.includes(segmentNumber)) {
|
||||||
|
validIndexList.push(index);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const avg = array => array.reduce((a, b) => a + b) / array.length;
|
||||||
|
const average = avg(validIndexList);
|
||||||
|
const closest = validIndexList.reduce((prev, curr) => {
|
||||||
|
return Math.abs(curr - average) < Math.abs(prev - average)
|
||||||
|
? curr
|
||||||
|
: prev;
|
||||||
|
});
|
||||||
|
|
||||||
|
const enabledElements = cs.getEnabledElements();
|
||||||
|
const element = enabledElements[activeViewportIndex].element;
|
||||||
|
|
||||||
|
const toolState = csTools.getToolState(element, 'stack');
|
||||||
|
if (!toolState) return;
|
||||||
|
|
||||||
|
const imageIds = toolState.data[0].imageIds;
|
||||||
|
const imageId = imageIds[closest];
|
||||||
|
const frameIndex = imageIds.indexOf(imageId);
|
||||||
|
const SOPInstanceUID = cs.metaData.get('SOPInstanceUID', imageId);
|
||||||
|
|
||||||
|
cs.getEnabledElements().forEach(enabledElement => {
|
||||||
|
cs.updateImage(enabledElement.element);
|
||||||
|
});
|
||||||
|
|
||||||
|
DICOMSegTempCrosshairsTool.addCrosshair(
|
||||||
|
element,
|
||||||
|
imageId,
|
||||||
|
segmentNumber
|
||||||
|
);
|
||||||
|
|
||||||
|
cs.getEnabledElements().forEach(enabledElement => {
|
||||||
|
cs.updateImage(enabledElement.element);
|
||||||
|
});
|
||||||
|
|
||||||
|
const refreshViewports = false;
|
||||||
|
|
||||||
|
commandsManager.runCommand('jumpToImage', {
|
||||||
|
StudyInstanceUID,
|
||||||
|
SOPInstanceUID,
|
||||||
|
frameIndex,
|
||||||
|
activeViewportIndex,
|
||||||
|
refreshViewports,
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.log('Error in moving to the first segment slice');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const definitions = {
|
||||||
|
jumpToFirstSegment: {
|
||||||
|
commandFn: actions.jumpToFirstSegment,
|
||||||
|
storeContexts: ['viewports'],
|
||||||
|
options: {},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
definitions,
|
||||||
|
defaultContext: 'VIEWER',
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default commandsModule;
|
||||||
@@ -62,8 +62,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.dcmseg-segment-item .segment-label span {
|
.dcmseg-segment-item .segment-label span {
|
||||||
overflow-wrap: normal;
|
overflow-wrap: break-word;
|
||||||
white-space: nowrap;
|
white-space: normal;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
max-width: calc(100% - 40px); /* calc(100% - 50px); 20px = eye icon */
|
max-width: calc(100% - 40px); /* calc(100% - 50px); 20px = eye icon */
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
@@ -71,6 +71,7 @@
|
|||||||
|
|
||||||
.dcmseg-segment-item .segment-label .eye-icon {
|
.dcmseg-segment-item .segment-label .eye-icon {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
min-width: 20px;
|
||||||
color: var(--active-color);
|
color: var(--active-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+36
-35
@@ -1,4 +1,4 @@
|
|||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState, useEffect, useCallback } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import cornerstoneTools from 'cornerstone-tools';
|
import cornerstoneTools from 'cornerstone-tools';
|
||||||
import cornerstone from 'cornerstone-core';
|
import cornerstone from 'cornerstone-core';
|
||||||
@@ -36,6 +36,7 @@ const { studyMetadataManager } = utils;
|
|||||||
* @param {Function} props.onConfigurationChange - Configuration change handler
|
* @param {Function} props.onConfigurationChange - Configuration change handler
|
||||||
* @param {Function} props.activeContexts - List of active application contexts
|
* @param {Function} props.activeContexts - List of active application contexts
|
||||||
* @param {Function} props.contexts - List of available application contexts
|
* @param {Function} props.contexts - List of available application contexts
|
||||||
|
* @param {Function} props.servicesManager - Services manager
|
||||||
* @returns component
|
* @returns component
|
||||||
*/
|
*/
|
||||||
const SegmentationPanel = ({
|
const SegmentationPanel = ({
|
||||||
@@ -50,6 +51,7 @@ const SegmentationPanel = ({
|
|||||||
onSelectedSegmentationChange,
|
onSelectedSegmentationChange,
|
||||||
activeContexts = [],
|
activeContexts = [],
|
||||||
contexts = {},
|
contexts = {},
|
||||||
|
servicesManager,
|
||||||
}) => {
|
}) => {
|
||||||
const isVTK = () => activeContexts.includes(contexts.VTK);
|
const isVTK = () => activeContexts.includes(contexts.VTK);
|
||||||
const isCornerstone = () => activeContexts.includes(contexts.CORNERSTONE);
|
const isCornerstone = () => activeContexts.includes(contexts.CORNERSTONE);
|
||||||
@@ -59,6 +61,9 @@ const SegmentationPanel = ({
|
|||||||
* store with context to make these kind of things less blurry.
|
* store with context to make these kind of things less blurry.
|
||||||
*/
|
*/
|
||||||
const { configuration } = cornerstoneTools.getModule('segmentation');
|
const { configuration } = cornerstoneTools.getModule('segmentation');
|
||||||
|
if (configuration.segsTolerance === undefined) {
|
||||||
|
configuration.segsTolerance = 1e-2;
|
||||||
|
}
|
||||||
const DEFAULT_BRUSH_RADIUS = configuration.radius || 10;
|
const DEFAULT_BRUSH_RADIUS = configuration.radius || 10;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -87,6 +92,25 @@ const SegmentationPanel = ({
|
|||||||
return studyMetadata.getFirstImageId(displaySetInstanceUID);
|
return studyMetadata.getFirstImageId(displaySetInstanceUID);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const getAllSegDisplaySets = () => {
|
||||||
|
const { StudyInstanceUID } = getActiveViewport();
|
||||||
|
const studyMetadata = studyMetadataManager.get(StudyInstanceUID);
|
||||||
|
return studyMetadata.getDerivedDatasets({
|
||||||
|
Modality: 'SEG',
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const updateSegDisplaySetsTolerance = tolerance => {
|
||||||
|
const segDisplaySets = getAllSegDisplaySets();
|
||||||
|
segDisplaySets.forEach(segDisplaySet => {
|
||||||
|
// update tol value
|
||||||
|
segDisplaySet.tolerance = tolerance;
|
||||||
|
// reset load flags for allowing retry for seg parsing.
|
||||||
|
segDisplaySet.isLoaded = false;
|
||||||
|
segDisplaySet.loadError = false;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
const getActiveLabelMaps3D = () => {
|
const getActiveLabelMaps3D = () => {
|
||||||
const { labelmaps3D, activeLabelmapIndex } = getBrushStackState();
|
const { labelmaps3D, activeLabelmapIndex } = getBrushStackState();
|
||||||
return labelmaps3D[activeLabelmapIndex];
|
return labelmaps3D[activeLabelmapIndex];
|
||||||
@@ -224,33 +248,15 @@ const SegmentationPanel = ({
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
}, [
|
}, [activeIndex, viewports]);
|
||||||
activeIndex,
|
|
||||||
updateSegmentationComboBox,
|
|
||||||
viewports,
|
|
||||||
]);
|
|
||||||
|
|
||||||
const updateSegmentationComboBox = e => {
|
const updateSegmentationComboBox = e => {
|
||||||
const index = e.detail.activatedLabelmapIndex;
|
const index = e.detail.activatedLabelmapIndex;
|
||||||
if (index !== -1) {
|
if (index !== -1) {
|
||||||
setState(state => ({ ...state, selectedSegmentation: index }));
|
setState(state => ({ ...state, selectedSegmentation: index }));
|
||||||
} else {
|
|
||||||
cleanSegmentationComboBox();
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const cleanSegmentationComboBox = () => {
|
|
||||||
setState(state => ({
|
|
||||||
...state,
|
|
||||||
segmentsHidden: [],
|
|
||||||
segmentNumbers: [],
|
|
||||||
labelMapList: [],
|
|
||||||
segmentList: [],
|
|
||||||
isDisabled: true,
|
|
||||||
selectedSegmentation: -1,
|
|
||||||
}));
|
|
||||||
};
|
|
||||||
|
|
||||||
const refreshSegmentations = () => {
|
const refreshSegmentations = () => {
|
||||||
const activeViewport = getActiveViewport();
|
const activeViewport = getActiveViewport();
|
||||||
const isDisabled = !activeViewport || !activeViewport.StudyInstanceUID;
|
const isDisabled = !activeViewport || !activeViewport.StudyInstanceUID;
|
||||||
@@ -616,31 +622,25 @@ const SegmentationPanel = ({
|
|||||||
configuration.outlineWidth = newConfiguration.outlineWidth;
|
configuration.outlineWidth = newConfiguration.outlineWidth;
|
||||||
configuration.fillAlphaInactive = newConfiguration.fillAlphaInactive;
|
configuration.fillAlphaInactive = newConfiguration.fillAlphaInactive;
|
||||||
configuration.outlineAlphaInactive = newConfiguration.outlineAlphaInactive;
|
configuration.outlineAlphaInactive = newConfiguration.outlineAlphaInactive;
|
||||||
|
configuration.segsTolerance = newConfiguration.segsTolerance;
|
||||||
onConfigurationChange(newConfiguration);
|
onConfigurationChange(newConfiguration);
|
||||||
|
updateSegDisplaySetsTolerance(configuration.segsTolerance);
|
||||||
refreshViewports();
|
refreshViewports();
|
||||||
};
|
};
|
||||||
|
|
||||||
const onVisibilityChangeHandler = isVisible => {
|
const onVisibilityChangeHandler = isVisible => {
|
||||||
let segmentsHidden = [];
|
let segmentsHidden = [];
|
||||||
|
const labelmap3D = getActiveLabelMaps3D();
|
||||||
|
|
||||||
state.segmentNumbers.forEach(segmentNumber => {
|
state.segmentNumbers.forEach(segmentNumber => {
|
||||||
if (isVTK()) {
|
if (isVTK()) {
|
||||||
onSegmentVisibilityChange(segmentNumber, isVisible);
|
onSegmentVisibilityChange(segmentNumber, isVisible);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Get all labelmaps with this segmentNumber (overlapping segments) */
|
labelmap3D.segmentsHidden[segmentNumber] = !isVisible;
|
||||||
const { labelmaps3D } = getBrushStackState();
|
segmentsHidden = [
|
||||||
const possibleLabelMaps3D = labelmaps3D.filter(({ labelmaps2D }) => {
|
...new Set([...segmentsHidden, ...labelmap3D.segmentsHidden]),
|
||||||
return labelmaps2D.some(({ segmentsOnLabelmap }) =>
|
];
|
||||||
segmentsOnLabelmap.includes(segmentNumber)
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
possibleLabelMaps3D.forEach(labelmap3D => {
|
|
||||||
labelmap3D.segmentsHidden[segmentNumber] = !isVisible;
|
|
||||||
segmentsHidden = [
|
|
||||||
...new Set([...segmentsHidden, ...labelmap3D.segmentsHidden]),
|
|
||||||
];
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
setState(state => ({ ...state, segmentsHidden }));
|
setState(state => ({ ...state, segmentsHidden }));
|
||||||
@@ -665,6 +665,7 @@ const SegmentationPanel = ({
|
|||||||
configuration={configuration}
|
configuration={configuration}
|
||||||
onBack={() => setState(state => ({ ...state, showSettings: false }))}
|
onBack={() => setState(state => ({ ...state, showSettings: false }))}
|
||||||
onChange={updateConfiguration}
|
onChange={updateConfiguration}
|
||||||
|
servicesManager={servicesManager}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
@@ -709,7 +710,7 @@ const SegmentationPanel = ({
|
|||||||
count={state.segmentList.length}
|
count={state.segmentList.length}
|
||||||
isVisible={
|
isVisible={
|
||||||
state.segmentsHidden.filter(isHidden => isHidden === true).length <
|
state.segmentsHidden.filter(isHidden => isHidden === true).length <
|
||||||
state.segmentNumbers.length
|
state.segmentNumbers.length && state.segmentNumbers.length > 0
|
||||||
}
|
}
|
||||||
onVisibilityChange={onVisibilityChangeHandler}
|
onVisibilityChange={onVisibilityChangeHandler}
|
||||||
>
|
>
|
||||||
|
|||||||
+59
-6
@@ -4,7 +4,7 @@ import { Range } from '@ohif/ui';
|
|||||||
|
|
||||||
import './SegmentationSettings.css';
|
import './SegmentationSettings.css';
|
||||||
|
|
||||||
const SegmentationSettings = ({ configuration, onBack, onChange, disabledFields = [] }) => {
|
const SegmentationSettings = ({ configuration, onBack, onChange, servicesManager, disabledFields = [] }) => {
|
||||||
const [state, setState] = useState({
|
const [state, setState] = useState({
|
||||||
renderFill: configuration.renderFill,
|
renderFill: configuration.renderFill,
|
||||||
renderOutline: configuration.renderOutline,
|
renderOutline: configuration.renderOutline,
|
||||||
@@ -13,7 +13,8 @@ const SegmentationSettings = ({ configuration, onBack, onChange, disabledFields
|
|||||||
outlineAlpha: configuration.outlineAlpha,
|
outlineAlpha: configuration.outlineAlpha,
|
||||||
outlineWidth: configuration.outlineWidth,
|
outlineWidth: configuration.outlineWidth,
|
||||||
fillAlphaInactive: configuration.fillAlphaInactive,
|
fillAlphaInactive: configuration.fillAlphaInactive,
|
||||||
outlineAlphaInactive: configuration.outlineAlphaInactive
|
outlineAlphaInactive: configuration.outlineAlphaInactive,
|
||||||
|
segsTolerance: configuration.segsTolerance,
|
||||||
});
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -28,6 +29,32 @@ const SegmentationSettings = ({ configuration, onBack, onChange, disabledFields
|
|||||||
setState(state => ({ ...state, [field]: value }));
|
setState(state => ({ ...state, [field]: value }));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const once = fn => (...args) => {
|
||||||
|
if (!fn) return;
|
||||||
|
fn(...args);
|
||||||
|
fn = null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const segTolValue = document.getElementById('segToleranceValue');
|
||||||
|
if (segTolValue) {
|
||||||
|
segTolValue.onchange = once(function() {
|
||||||
|
const { UINotificationService, LoggerService } = servicesManager.services;
|
||||||
|
|
||||||
|
const error = new Error(
|
||||||
|
'Segmentation loader tolerance changed.\
|
||||||
|
This operation can potentially generate errors in the Segmentation parsing.'
|
||||||
|
);
|
||||||
|
|
||||||
|
LoggerService.error({ error, message: error.message });
|
||||||
|
UINotificationService.show({
|
||||||
|
title: 'Segmentation panel',
|
||||||
|
message: error.message,
|
||||||
|
type: 'warning',
|
||||||
|
autoClose: true,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const toFloat = value => parseFloat(value / 100).toFixed(2);
|
const toFloat = value => parseFloat(value / 100).toFixed(2);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -133,6 +160,31 @@ const SegmentationSettings = ({ configuration, onBack, onChange, disabledFields
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
<div className="settings-group" style={{ marginBottom: 15 }}>
|
||||||
|
<label style={{ margin: '0 15px' }}>
|
||||||
|
Tolerance:
|
||||||
|
<input
|
||||||
|
id="segToleranceValue"
|
||||||
|
style={{ margin: '0 15px' }}
|
||||||
|
label="Tolerance"
|
||||||
|
onKeyPress={event => {
|
||||||
|
const validate = string => {
|
||||||
|
let rgx = /[^-.e0-9]+/g;
|
||||||
|
return string.match(rgx);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (validate(event.key)) {
|
||||||
|
event.preventDefault();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onChange={event => {
|
||||||
|
save('segsTolerance', event.target.value);
|
||||||
|
}}
|
||||||
|
value={state.segsTolerance}
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -169,11 +221,12 @@ SegmentationSettings.propTypes = {
|
|||||||
renderFill: PropTypes.bool.isRequired,
|
renderFill: PropTypes.bool.isRequired,
|
||||||
renderOutline: PropTypes.bool.isRequired,
|
renderOutline: PropTypes.bool.isRequired,
|
||||||
shouldRenderInactiveLabelmaps: PropTypes.bool.isRequired,
|
shouldRenderInactiveLabelmaps: PropTypes.bool.isRequired,
|
||||||
fillAlpha: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired, /* TODO: why fillAlpha is string? */
|
fillAlpha: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired,
|
||||||
outlineAlpha: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired, /* TODO: why fillAlpha is string? */
|
outlineAlpha: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired,
|
||||||
outlineWidth: PropTypes.number.isRequired,
|
outlineWidth: PropTypes.number.isRequired,
|
||||||
fillAlphaInactive: PropTypes.number.isRequired,
|
fillAlphaInactive: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired,
|
||||||
outlineAlphaInactive: PropTypes.number.isRequired,
|
outlineAlphaInactive: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired,
|
||||||
|
segsTolerance: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired,
|
||||||
}).isRequired,
|
}).isRequired,
|
||||||
onBack: PropTypes.func.isRequired,
|
onBack: PropTypes.func.isRequired,
|
||||||
onChange: PropTypes.func.isRequired,
|
onChange: PropTypes.func.isRequired,
|
||||||
|
|||||||
@@ -62,6 +62,7 @@ export default function getSopClassHandlerModule({ servicesManager }) {
|
|||||||
SeriesNumber,
|
SeriesNumber,
|
||||||
SeriesDescription,
|
SeriesDescription,
|
||||||
metadata,
|
metadata,
|
||||||
|
tolerance: 1e-2,
|
||||||
};
|
};
|
||||||
|
|
||||||
segDisplaySet.getSourceDisplaySet = function(
|
segDisplaySet.getSourceDisplaySet = function(
|
||||||
@@ -93,7 +94,7 @@ export default function getSopClassHandlerModule({ servicesManager }) {
|
|||||||
referencedDisplaySet.SeriesInstanceUID
|
referencedDisplaySet.SeriesInstanceUID
|
||||||
);
|
);
|
||||||
|
|
||||||
const results = await _parseSeg(segArrayBuffer, imageIds);
|
const results = await _parseSeg(segArrayBuffer, imageIds, segDisplaySet.tolerance);
|
||||||
if (results === undefined) {
|
if (results === undefined) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -149,10 +150,13 @@ export default function getSopClassHandlerModule({ servicesManager }) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function _parseSeg(arrayBuffer, imageIds) {
|
function _parseSeg(
|
||||||
const skipOverlapping = false;
|
arrayBuffer,
|
||||||
const tolerance = 1e-2;
|
imageIds,
|
||||||
const cornerstoneToolsVersion = 4;
|
tolerance = 1e-2,
|
||||||
|
skipOverlapping = false,
|
||||||
|
cornerstoneToolsVersion = 4
|
||||||
|
) {
|
||||||
return dcmjs.adapters.Cornerstone.Segmentation.generateToolState(
|
return dcmjs.adapters.Cornerstone.Segmentation.generateToolState(
|
||||||
imageIds,
|
imageIds,
|
||||||
arrayBuffer,
|
arrayBuffer,
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import toolbarModule from './toolbarModule.js';
|
|||||||
import getSopClassHandlerModule from './getOHIFDicomSegSopClassHandler.js';
|
import getSopClassHandlerModule from './getOHIFDicomSegSopClassHandler.js';
|
||||||
import SegmentationPanel from './components/SegmentationPanel/SegmentationPanel.js';
|
import SegmentationPanel from './components/SegmentationPanel/SegmentationPanel.js';
|
||||||
import { version } from '../package.json';
|
import { version } from '../package.json';
|
||||||
|
import commandsModule from './commandsModule.js';
|
||||||
const { studyMetadataManager } = OHIF.utils;
|
const { studyMetadataManager } = OHIF.utils;
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
@@ -32,12 +33,18 @@ export default {
|
|||||||
|
|
||||||
const ExtendedSegmentationPanel = props => {
|
const ExtendedSegmentationPanel = props => {
|
||||||
const { activeContexts } = api.hooks.useAppContext();
|
const { activeContexts } = api.hooks.useAppContext();
|
||||||
|
|
||||||
const onDisplaySetLoadFailureHandler = error => {
|
const onDisplaySetLoadFailureHandler = error => {
|
||||||
LoggerService.error({ error, message: error.message });
|
const message =
|
||||||
|
error.message.includes('orthogonal') ||
|
||||||
|
error.message.includes('oblique')
|
||||||
|
? 'The segmentation has been detected as non coplanar,\
|
||||||
|
If you really think it is coplanar,\
|
||||||
|
please adjust the tolerance in the segmentation panel settings (at your own peril!)'
|
||||||
|
: error.message;
|
||||||
|
LoggerService.error({ error, message });
|
||||||
UINotificationService.show({
|
UINotificationService.show({
|
||||||
title: 'DICOM Segmentation Loader',
|
title: 'DICOM Segmentation Loader',
|
||||||
message: error.message,
|
message,
|
||||||
type: 'error',
|
type: 'error',
|
||||||
autoClose: false,
|
autoClose: false,
|
||||||
});
|
});
|
||||||
@@ -78,6 +85,7 @@ export default {
|
|||||||
onConfigurationChange={onConfigurationChangeHandler}
|
onConfigurationChange={onConfigurationChangeHandler}
|
||||||
onSelectedSegmentationChange={onSelectedSegmentationChangeHandler}
|
onSelectedSegmentationChange={onSelectedSegmentationChangeHandler}
|
||||||
onDisplaySetLoadFailure={onDisplaySetLoadFailureHandler}
|
onDisplaySetLoadFailure={onDisplaySetLoadFailureHandler}
|
||||||
|
servicesManager={servicesManager}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -114,6 +122,15 @@ export default {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const onSegmentationsCompletelyLoaded = () => {
|
||||||
|
commandsManager.runCommand('jumpToFirstSegment');
|
||||||
|
};
|
||||||
|
|
||||||
|
document.addEventListener(
|
||||||
|
'segseriesselected',
|
||||||
|
onSegmentationsCompletelyLoaded
|
||||||
|
);
|
||||||
|
|
||||||
document.addEventListener(
|
document.addEventListener(
|
||||||
'extensiondicomsegmentationsegloaded',
|
'extensiondicomsegmentationsegloaded',
|
||||||
onSegmentationsLoaded
|
onSegmentationsLoaded
|
||||||
@@ -175,5 +192,8 @@ export default {
|
|||||||
defaultContext: ['VIEWER'],
|
defaultContext: ['VIEWER'],
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
getCommandsModule({ commandsManager, servicesManager }) {
|
||||||
|
return commandsModule({ commandsManager, servicesManager });
|
||||||
|
},
|
||||||
getSopClassHandlerModule,
|
getSopClassHandlerModule,
|
||||||
};
|
};
|
||||||
@@ -47,14 +47,23 @@ export default async function setActiveLabelmap(
|
|||||||
return labelmapIndex;
|
return labelmapIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!displaySet.isLoaded) {
|
if (!displaySet.isLoaded && !displaySet.loadError) {
|
||||||
try {
|
try {
|
||||||
await displaySet.load(referencedDisplaySet, studies);
|
await displaySet.load(referencedDisplaySet, studies);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
displaySet.isLoaded = false;
|
displaySet.isLoaded = false;
|
||||||
displaySet.loadError = true;
|
displaySet.loadError = true;
|
||||||
|
displaySet.segLoadErrorMessagge = error.message;
|
||||||
onDisplaySetLoadFailure(error);
|
onDisplaySetLoadFailure(error);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* TODO: Improve the way we notify parts of the app
|
||||||
|
* that depends on derived display sets to be loaded.
|
||||||
|
* (Implement pubsub for better tracking of derived display sets)
|
||||||
|
*/
|
||||||
|
const event = new CustomEvent('segmentationLoadingError');
|
||||||
|
document.dispatchEvent(event);
|
||||||
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,54 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
## [0.2.26](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-tag-browser@0.2.25...@ohif/extension-dicom-tag-browser@0.2.26) (2023-03-19)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-dicom-tag-browser
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [0.2.25](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-tag-browser@0.2.24...@ohif/extension-dicom-tag-browser@0.2.25) (2022-12-21)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-dicom-tag-browser
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [0.2.24](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-tag-browser@0.2.23...@ohif/extension-dicom-tag-browser@0.2.24) (2022-09-01)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-dicom-tag-browser
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [0.2.23](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-tag-browser@0.2.22...@ohif/extension-dicom-tag-browser@0.2.23) (2022-04-06)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-dicom-tag-browser
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [0.2.22](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-tag-browser@0.2.21...@ohif/extension-dicom-tag-browser@0.2.22) (2022-04-05)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-dicom-tag-browser
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [0.2.21](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-tag-browser@0.2.20...@ohif/extension-dicom-tag-browser@0.2.21) (2022-03-25)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-dicom-tag-browser
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## [0.2.20](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-tag-browser@0.2.19...@ohif/extension-dicom-tag-browser@0.2.20) (2022-03-14)
|
## [0.2.20](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-tag-browser@0.2.19...@ohif/extension-dicom-tag-browser@0.2.20) (2022-03-14)
|
||||||
|
|
||||||
**Note:** Version bump only for package @ohif/extension-dicom-tag-browser
|
**Note:** Version bump only for package @ohif/extension-dicom-tag-browser
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@ohif/extension-dicom-tag-browser",
|
"name": "@ohif/extension-dicom-tag-browser",
|
||||||
"version": "0.2.20",
|
"version": "0.2.26",
|
||||||
"description": "OHIF extension for checking DICOM headers.",
|
"description": "OHIF extension for checking DICOM headers.",
|
||||||
"author": "OHIF",
|
"author": "OHIF",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
@@ -28,13 +28,13 @@
|
|||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@ohif/core": "^2.6.0",
|
"@ohif/core": "^2.6.0",
|
||||||
"dcmjs": "0.19.7",
|
"dcmjs": "0.29.3",
|
||||||
"react": "^16.8.6"
|
"react": "^16.8.6"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "^7.5.5",
|
"@babel/runtime": "^7.5.5",
|
||||||
"dicomweb-client": "^0.8.3",
|
"dicomweb-client": "^0.8.3",
|
||||||
"moment": "2.24.0",
|
"moment": "2.29.4",
|
||||||
"react-select": "^3.0.8"
|
"react-select": "^3.0.8"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -59,7 +59,7 @@ const DicomTagBrowser = ({ displaySets, displaySetInstanceUID }) => {
|
|||||||
let metadata;
|
let metadata;
|
||||||
const isImageStack =
|
const isImageStack =
|
||||||
activeDisplaySet instanceof ImageSet &&
|
activeDisplaySet instanceof ImageSet &&
|
||||||
activeDisplaySet.isModalitySupported === true;
|
activeDisplaySet.isSOPClassUIDSupported === true;
|
||||||
|
|
||||||
let instanceList;
|
let instanceList;
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,14 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
## [0.2.2](https://github.com/OHIF/Viewers/compare/@ohif/extension-lesion-tracker@0.2.1...@ohif/extension-lesion-tracker@0.2.2) (2022-06-29)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-lesion-tracker
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## [0.2.1](https://github.com/OHIF/Viewers/compare/@ohif/extension-lesion-tracker@0.2.0...@ohif/extension-lesion-tracker@0.2.1) (2020-09-03)
|
## [0.2.1](https://github.com/OHIF/Viewers/compare/@ohif/extension-lesion-tracker@0.2.0...@ohif/extension-lesion-tracker@0.2.1) (2020-09-03)
|
||||||
|
|
||||||
**Note:** Version bump only for package @ohif/extension-lesion-tracker
|
**Note:** Version bump only for package @ohif/extension-lesion-tracker
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@ohif/extension-lesion-tracker",
|
"name": "@ohif/extension-lesion-tracker",
|
||||||
"version": "0.2.1",
|
"version": "0.2.2",
|
||||||
"description": "OHIF extension for Lesion Tracker",
|
"description": "OHIF extension for Lesion Tracker",
|
||||||
"author": "OHIF",
|
"author": "OHIF",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
|||||||
@@ -76,13 +76,13 @@ const additionalFindings = [
|
|||||||
|
|
||||||
const currentCollections = [
|
const currentCollections = [
|
||||||
{
|
{
|
||||||
selectorAction: () => { },
|
selectorAction: () => {},
|
||||||
maxMeasurements: 3,
|
maxMeasurements: 3,
|
||||||
groupName: 'Measurements',
|
groupName: 'Measurements',
|
||||||
measurements: measurements,
|
measurements: measurements,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
selectorAction: () => { },
|
selectorAction: () => {},
|
||||||
groupName: 'Additional Findings',
|
groupName: 'Additional Findings',
|
||||||
measurements: additionalFindings,
|
measurements: additionalFindings,
|
||||||
},
|
},
|
||||||
@@ -90,13 +90,13 @@ const currentCollections = [
|
|||||||
|
|
||||||
const comparisonColletions = [
|
const comparisonColletions = [
|
||||||
{
|
{
|
||||||
selectorAction: () => { },
|
selectorAction: () => {},
|
||||||
maxMeasurements: 3,
|
maxMeasurements: 3,
|
||||||
groupName: 'Measurements',
|
groupName: 'Measurements',
|
||||||
measurements: measurements,
|
measurements: measurements,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
selectorAction: () => { },
|
selectorAction: () => {},
|
||||||
groupName: 'Additional Findings',
|
groupName: 'Additional Findings',
|
||||||
measurements: additionalFindings,
|
measurements: additionalFindings,
|
||||||
},
|
},
|
||||||
@@ -128,7 +128,7 @@ const comparisonTimepoints = [
|
|||||||
{
|
{
|
||||||
key: 'Comparison',
|
key: 'Comparison',
|
||||||
date: '15-Jun-18',
|
date: '15-Jun-18',
|
||||||
}
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const MeasurementComparisonTable = () => {
|
const MeasurementComparisonTable = () => {
|
||||||
@@ -138,8 +138,8 @@ const MeasurementComparisonTable = () => {
|
|||||||
timepoints={comparisonTimepoints}
|
timepoints={comparisonTimepoints}
|
||||||
overallWarnings={overallWarnings}
|
overallWarnings={overallWarnings}
|
||||||
measurementCollection={comparisonCollections}
|
measurementCollection={comparisonCollections}
|
||||||
onRelabelClick={() => { }}
|
onRelabelClick={() => {}}
|
||||||
onEditDescriptionClick={() => { }}
|
onEditDescriptionClick={() => {}}
|
||||||
/>
|
/>
|
||||||
<div className="generate-report">
|
<div className="generate-report">
|
||||||
<button className="btn btn-primary">Generate Report</button>
|
<button className="btn btn-primary">Generate Report</button>
|
||||||
|
|||||||
@@ -3,6 +3,190 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
## [1.12.42](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.12.41...@ohif/extension-vtk@1.12.42) (2023-03-19)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-vtk
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [1.12.41](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.12.40...@ohif/extension-vtk@1.12.41) (2022-12-21)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-vtk
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [1.12.40](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.12.39...@ohif/extension-vtk@1.12.40) (2022-12-12)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-vtk
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [1.12.39](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.12.38...@ohif/extension-vtk@1.12.39) (2022-11-12)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-vtk
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [1.12.38](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.12.37...@ohif/extension-vtk@1.12.38) (2022-11-02)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-vtk
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [1.12.37](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.12.36...@ohif/extension-vtk@1.12.37) (2022-10-28)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-vtk
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [1.12.36](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.12.35...@ohif/extension-vtk@1.12.36) (2022-10-25)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-vtk
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [1.12.35](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.12.34...@ohif/extension-vtk@1.12.35) (2022-09-26)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-vtk
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [1.12.34](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.12.33...@ohif/extension-vtk@1.12.34) (2022-09-20)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-vtk
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [1.12.33](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.12.32...@ohif/extension-vtk@1.12.33) (2022-09-19)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-vtk
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [1.12.32](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.12.31...@ohif/extension-vtk@1.12.32) (2022-09-12)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-vtk
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [1.12.31](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.12.30...@ohif/extension-vtk@1.12.31) (2022-09-02)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-vtk
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [1.12.30](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.12.29...@ohif/extension-vtk@1.12.30) (2022-09-01)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-vtk
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [1.12.29](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.12.28...@ohif/extension-vtk@1.12.29) (2022-08-31)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-vtk
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [1.12.28](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.12.27...@ohif/extension-vtk@1.12.28) (2022-08-24)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-vtk
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [1.12.27](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.12.26...@ohif/extension-vtk@1.12.27) (2022-06-30)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-vtk
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [1.12.26](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.12.25...@ohif/extension-vtk@1.12.26) (2022-06-29)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-vtk
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [1.12.25](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.12.24...@ohif/extension-vtk@1.12.25) (2022-06-28)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-vtk
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [1.12.24](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.12.23...@ohif/extension-vtk@1.12.24) (2022-06-14)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-vtk
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [1.12.23](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.12.22...@ohif/extension-vtk@1.12.23) (2022-04-22)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-vtk
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [1.12.22](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.12.21...@ohif/extension-vtk@1.12.22) (2022-04-06)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-vtk
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [1.12.21](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.12.20...@ohif/extension-vtk@1.12.21) (2022-04-05)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-vtk
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [1.12.20](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.12.19...@ohif/extension-vtk@1.12.20) (2022-03-25)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/extension-vtk
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## [1.12.19](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.12.18...@ohif/extension-vtk@1.12.19) (2022-03-14)
|
## [1.12.19](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.12.18...@ohif/extension-vtk@1.12.19) (2022-03-14)
|
||||||
|
|
||||||
**Note:** Version bump only for package @ohif/extension-vtk
|
**Note:** Version bump only for package @ohif/extension-vtk
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@ohif/extension-vtk",
|
"name": "@ohif/extension-vtk",
|
||||||
"version": "1.12.19",
|
"version": "1.12.42",
|
||||||
"description": "OHIF extension for VTK.js",
|
"description": "OHIF extension for VTK.js",
|
||||||
"author": "OHIF",
|
"author": "OHIF",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
@@ -34,8 +34,8 @@
|
|||||||
"@ohif/ui": "^0.50.0",
|
"@ohif/ui": "^0.50.0",
|
||||||
"cornerstone-core": "^2.6.1",
|
"cornerstone-core": "^2.6.1",
|
||||||
"cornerstone-tools": "^6.0.6",
|
"cornerstone-tools": "^6.0.6",
|
||||||
"cornerstone-wado-image-loader": "^4.1.0",
|
"cornerstone-wado-image-loader": "^4.2.1",
|
||||||
"dcmjs": "0.19.7",
|
"dcmjs": "0.29.3",
|
||||||
"dicom-parser": "^1.8.11",
|
"dicom-parser": "^1.8.11",
|
||||||
"i18next": "^17.0.3",
|
"i18next": "^17.0.3",
|
||||||
"i18next-browser-languagedetector": "^3.0.1",
|
"i18next-browser-languagedetector": "^3.0.1",
|
||||||
@@ -50,13 +50,13 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "^7.5.5",
|
"@babel/runtime": "^7.5.5",
|
||||||
"lodash.throttle": "^4.1.1",
|
"lodash.throttle": "^4.1.1",
|
||||||
"react-vtkjs-viewport": "^0.14.4"
|
"react-vtkjs-viewport": "^0.14.5"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@ohif/core": "^2.16.14",
|
"@ohif/core": "^2.16.35",
|
||||||
"@ohif/ui": "^1.10.7",
|
"@ohif/ui": "^1.10.11",
|
||||||
"cornerstone-tools": "^6.0.6",
|
"cornerstone-tools": "^6.0.6",
|
||||||
"cornerstone-wado-image-loader": "^4.1.0",
|
"cornerstone-wado-image-loader": "^4.2.1",
|
||||||
"dicom-parser": "^1.8.11",
|
"dicom-parser": "^1.8.11",
|
||||||
"gh-pages": "^2.0.1",
|
"gh-pages": "^2.0.1",
|
||||||
"i18next": "^17.0.3",
|
"i18next": "^17.0.3",
|
||||||
|
|||||||
@@ -99,6 +99,7 @@
|
|||||||
"shader-loader": "^1.3.1",
|
"shader-loader": "^1.3.1",
|
||||||
"start-server-and-test": "^1.10.0",
|
"start-server-and-test": "^1.10.0",
|
||||||
"style-loader": "^1.0.0",
|
"style-loader": "^1.0.0",
|
||||||
|
"styled-components": "^5.3.5",
|
||||||
"stylelint": "^10.1.0",
|
"stylelint": "^10.1.0",
|
||||||
"stylelint-config-recommended": "^2.2.0",
|
"stylelint-config-recommended": "^2.2.0",
|
||||||
"stylus": "^0.54.5",
|
"stylus": "^0.54.5",
|
||||||
@@ -124,6 +125,14 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"resolutions": {
|
"resolutions": {
|
||||||
|
"handlebars": "4.7.7",
|
||||||
|
"json-schema": "0.4.0",
|
||||||
|
"ejs": "3.1.7",
|
||||||
|
"thenify": "3.3.1",
|
||||||
|
"parse-url": "8.1.0",
|
||||||
|
"loader-utils": "1.4.1",
|
||||||
|
"flat": "5.0.1",
|
||||||
|
"minimist": "1.2.8",
|
||||||
"**/node-gyp": "8.4.1",
|
"**/node-gyp": "8.4.1",
|
||||||
"**/@babel/runtime": "7.5.5"
|
"**/@babel/runtime": "7.5.5"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,189 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
## [2.16.35](https://github.com/OHIF/Viewers/compare/@ohif/core@2.16.34...@ohif/core@2.16.35) (2022-12-21)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/core
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [2.16.34](https://github.com/OHIF/Viewers/compare/@ohif/core@2.16.33...@ohif/core@2.16.34) (2022-12-12)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* [#2964](https://github.com/OHIF/Viewers/issues/2964) Reword message for segmentation error loading due to orientation tolerance ([#3017](https://github.com/OHIF/Viewers/issues/3017)) ([597ac11](https://github.com/OHIF/Viewers/commit/597ac11c4daee1b1e14148804551e20611cfef08))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [2.16.33](https://github.com/OHIF/Viewers/compare/@ohif/core@2.16.32...@ohif/core@2.16.33) (2022-11-12)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* handle missing ReferencedInstanceSequence attribute ([#2786](https://github.com/OHIF/Viewers/issues/2786)) ([4473807](https://github.com/OHIF/Viewers/commit/44738078dbd8dc6b27d2d669e25417e37371f48d))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [2.16.32](https://github.com/OHIF/Viewers/compare/@ohif/core@2.16.31...@ohif/core@2.16.32) (2022-11-02)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/core
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [2.16.31](https://github.com/OHIF/Viewers/compare/@ohif/core@2.16.30...@ohif/core@2.16.31) (2022-10-28)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* [#2964](https://github.com/OHIF/Viewers/issues/2964) Update message for segmentation error loading due to orientation tolerance ([#2982](https://github.com/OHIF/Viewers/issues/2982)) ([545161d](https://github.com/OHIF/Viewers/commit/545161d5e8d92bb6652f17dc24a36dac8aa7b3a4))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [2.16.30](https://github.com/OHIF/Viewers/compare/@ohif/core@2.16.29...@ohif/core@2.16.30) (2022-10-25)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* 2965 Correct Parsing Logic for Qualitative Instance Level SR ([#2972](https://github.com/OHIF/Viewers/issues/2972)) ([f7db74a](https://github.com/OHIF/Viewers/commit/f7db74a84376598065b6fa2c00cd5735415ff651))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [2.16.29](https://github.com/OHIF/Viewers/compare/@ohif/core@2.16.28...@ohif/core@2.16.29) (2022-09-20)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/core
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [2.16.28](https://github.com/OHIF/Viewers/compare/@ohif/core@2.16.27...@ohif/core@2.16.28) (2022-09-19)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/core
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [2.16.27](https://github.com/OHIF/Viewers/compare/@ohif/core@2.16.26...@ohif/core@2.16.27) (2022-09-12)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/core
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [2.16.26](https://github.com/OHIF/Viewers/compare/@ohif/core@2.16.25...@ohif/core@2.16.26) (2022-09-02)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/core
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [2.16.25](https://github.com/OHIF/Viewers/compare/@ohif/core@2.16.24...@ohif/core@2.16.25) (2022-09-01)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/core
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [2.16.24](https://github.com/OHIF/Viewers/compare/@ohif/core@2.16.23...@ohif/core@2.16.24) (2022-08-31)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/core
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [2.16.23](https://github.com/OHIF/Viewers/compare/@ohif/core@2.16.22...@ohif/core@2.16.23) (2022-08-24)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/core
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [2.16.22](https://github.com/OHIF/Viewers/compare/@ohif/core@2.16.21...@ohif/core@2.16.22) (2022-06-30)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/core
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [2.16.21](https://github.com/OHIF/Viewers/compare/@ohif/core@2.16.20...@ohif/core@2.16.21) (2022-06-29)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/core
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [2.16.20](https://github.com/OHIF/Viewers/compare/@ohif/core@2.16.19...@ohif/core@2.16.20) (2022-06-28)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* Re IDC2797, fix double parsing of SR qualitative annotations ([#2830](https://github.com/OHIF/Viewers/issues/2830)) ([9f813e0](https://github.com/OHIF/Viewers/commit/9f813e0ea08f0aada8efab9ef6228ad90dd1758d))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [2.16.19](https://github.com/OHIF/Viewers/compare/@ohif/core@2.16.18...@ohif/core@2.16.19) (2022-06-14)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/core
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [2.16.18](https://github.com/OHIF/Viewers/compare/@ohif/core@2.16.17...@ohif/core@2.16.18) (2022-04-22)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/core
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [2.16.17](https://github.com/OHIF/Viewers/compare/@ohif/core@2.16.16...@ohif/core@2.16.17) (2022-04-06)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/core
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [2.16.16](https://github.com/OHIF/Viewers/compare/@ohif/core@2.16.15...@ohif/core@2.16.16) (2022-04-05)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/core
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [2.16.15](https://github.com/OHIF/Viewers/compare/@ohif/core@2.16.14...@ohif/core@2.16.15) (2022-03-25)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/core
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## [2.16.14](https://github.com/OHIF/Viewers/compare/@ohif/core@2.16.13...@ohif/core@2.16.14) (2022-03-14)
|
## [2.16.14](https://github.com/OHIF/Viewers/compare/@ohif/core@2.16.13...@ohif/core@2.16.14) (2022-03-14)
|
||||||
|
|
||||||
**Note:** Version bump only for package @ohif/core
|
**Note:** Version bump only for package @ohif/core
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@ohif/core",
|
"name": "@ohif/core",
|
||||||
"version": "2.16.14",
|
"version": "2.16.35",
|
||||||
"description": "Generic business logic for web-based medical imaging applications",
|
"description": "Generic business logic for web-based medical imaging applications",
|
||||||
"author": "OHIF Core Team",
|
"author": "OHIF Core Team",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
@@ -33,13 +33,13 @@
|
|||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"cornerstone-core": "^2.6.1",
|
"cornerstone-core": "^2.6.1",
|
||||||
"cornerstone-tools": "^6.0.6",
|
"cornerstone-tools": "^6.0.6",
|
||||||
"cornerstone-wado-image-loader": "^4.1.0",
|
"cornerstone-wado-image-loader": "^4.2.1",
|
||||||
"dicom-parser": "^1.8.11"
|
"dicom-parser": "^1.8.11"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "^7.5.5",
|
"@babel/runtime": "^7.5.5",
|
||||||
"ajv": "^6.10.0",
|
"ajv": "^6.10.0",
|
||||||
"dcmjs": "0.19.7",
|
"dcmjs": "0.29.3",
|
||||||
"dicomweb-client": "^0.8.3",
|
"dicomweb-client": "^0.8.3",
|
||||||
"immer": "9.0.12",
|
"immer": "9.0.12",
|
||||||
"isomorphic-base64": "^1.0.2",
|
"isomorphic-base64": "^1.0.2",
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
export default {
|
export default {
|
||||||
|
TEXT: 'TEXT',
|
||||||
POINT: 'POINT',
|
POINT: 'POINT',
|
||||||
MULTIPOINT: 'MULTIPOINT',
|
MULTIPOINT: 'MULTIPOINT',
|
||||||
POLYLINE: 'POLYLINE',
|
POLYLINE: 'POLYLINE',
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import { ImageSet } from '../../classes';
|
import { ImageSet } from '../../classes';
|
||||||
import getMeasurements from './utils/getMeasurements';
|
import getMeasurements from './utils/getMeasurements';
|
||||||
import getReferencedImagesList from './utils/getReferencedImagesList';
|
|
||||||
import isRehydratable from './utils/isRehydratable';
|
import isRehydratable from './utils/isRehydratable';
|
||||||
import addMeasurement from './utils/addMeasurement';
|
import addMeasurement from './utils/addMeasurement';
|
||||||
|
|
||||||
@@ -8,6 +7,16 @@ const parseSCOORD3D = ({ servicesManager, displaySets }) => {
|
|||||||
const { MeasurementService } = servicesManager.services;
|
const { MeasurementService } = servicesManager.services;
|
||||||
|
|
||||||
const srDisplaySets = displaySets.filter(ds => ds.Modality === 'SR');
|
const srDisplaySets = displaySets.filter(ds => ds.Modality === 'SR');
|
||||||
|
const imageDisplaySets = displaySets.filter(
|
||||||
|
ds =>
|
||||||
|
ds.Modality !== 'SR' &&
|
||||||
|
ds.Modality !== 'SEG' &&
|
||||||
|
ds.Modality !== 'RTSTRUCT'
|
||||||
|
);
|
||||||
|
|
||||||
|
imageDisplaySets.forEach(imageDisplaySet => {
|
||||||
|
imageDisplaySet.SRLabels = [];
|
||||||
|
});
|
||||||
|
|
||||||
srDisplaySets.forEach(srDisplaySet => {
|
srDisplaySets.forEach(srDisplaySet => {
|
||||||
const firstInstance = srDisplaySet.metadata;
|
const firstInstance = srDisplaySet.metadata;
|
||||||
@@ -17,8 +26,7 @@ const parseSCOORD3D = ({ servicesManager, displaySets }) => {
|
|||||||
|
|
||||||
const { ContentSequence } = firstInstance;
|
const { ContentSequence } = firstInstance;
|
||||||
|
|
||||||
srDisplaySet.referencedImages = getReferencedImagesList(ContentSequence);
|
srDisplaySet.measurements = getMeasurements(ContentSequence);
|
||||||
srDisplaySet.measurements = getMeasurements(ContentSequence, srDisplaySet);
|
|
||||||
const mappings = MeasurementService.getSourceMappings(
|
const mappings = MeasurementService.getSourceMappings(
|
||||||
'CornerstoneTools',
|
'CornerstoneTools',
|
||||||
'4'
|
'4'
|
||||||
@@ -28,13 +36,6 @@ const parseSCOORD3D = ({ servicesManager, displaySets }) => {
|
|||||||
srDisplaySet.isRehydratable = isRehydratable(srDisplaySet, mappings);
|
srDisplaySet.isRehydratable = isRehydratable(srDisplaySet, mappings);
|
||||||
srDisplaySet.isLoaded = true;
|
srDisplaySet.isLoaded = true;
|
||||||
|
|
||||||
const imageDisplaySets = displaySets.filter(
|
|
||||||
ds =>
|
|
||||||
ds.Modality !== 'SR' &&
|
|
||||||
ds.Modality !== 'SEG' &&
|
|
||||||
ds.Modality !== 'RTSTRUCT' &&
|
|
||||||
ds.Modality !== 'RTDOSE'
|
|
||||||
);
|
|
||||||
imageDisplaySets.forEach(imageDisplaySet => {
|
imageDisplaySets.forEach(imageDisplaySet => {
|
||||||
// Check currently added displaySets and add measurements if the sources exist.
|
// Check currently added displaySets and add measurements if the sources exist.
|
||||||
checkIfCanAddMeasurementsToDisplaySet(srDisplaySet, imageDisplaySet);
|
checkIfCanAddMeasurementsToDisplaySet(srDisplaySet, imageDisplaySet);
|
||||||
@@ -127,10 +128,10 @@ const checkIfCanAddMeasurementsToDisplaySet = (
|
|||||||
|
|
||||||
const imageIds = images.map(i => i.getImageId());
|
const imageIds = images.map(i => i.getImageId());
|
||||||
const SOPInstanceUIDs = images.map(i => i.SOPInstanceUID);
|
const SOPInstanceUIDs = images.map(i => i.SOPInstanceUID);
|
||||||
|
const colors = new Map();
|
||||||
measurements.forEach(measurement => {
|
measurements.forEach(measurement => {
|
||||||
const { coords } = measurement;
|
const { coords } = measurement;
|
||||||
|
coords.forEach((coord, index) => {
|
||||||
coords.forEach(coord => {
|
|
||||||
if (coord.ReferencedSOPSequence !== undefined) {
|
if (coord.ReferencedSOPSequence !== undefined) {
|
||||||
const imageIndex = SOPInstanceUIDs.findIndex(
|
const imageIndex = SOPInstanceUIDs.findIndex(
|
||||||
SOPInstanceUID =>
|
SOPInstanceUID =>
|
||||||
@@ -138,14 +139,50 @@ const checkIfCanAddMeasurementsToDisplaySet = (
|
|||||||
coord.ReferencedSOPSequence.ReferencedSOPInstanceUID
|
coord.ReferencedSOPSequence.ReferencedSOPInstanceUID
|
||||||
);
|
);
|
||||||
if (imageIndex > -1) {
|
if (imageIndex > -1) {
|
||||||
|
if (!srDisplaySet.referencedDisplaySets.includes(imageDisplaySet)) {
|
||||||
|
srDisplaySet.referencedDisplaySets.push(imageDisplaySet);
|
||||||
|
}
|
||||||
|
|
||||||
const imageId = imageIds[imageIndex];
|
const imageId = imageIds[imageIndex];
|
||||||
const imageMetadata = images[imageIndex].getData().metadata;
|
const imageMetadata = images[imageIndex].getData().metadata;
|
||||||
addMeasurement(
|
|
||||||
measurement,
|
if (coord.GraphicType === 'TEXT') {
|
||||||
imageId,
|
const key =
|
||||||
imageMetadata,
|
measurement.labels[index].label + measurement.labels[index].value;
|
||||||
imageDisplaySet.displaySetInstanceUID
|
let color = colors.get(key);
|
||||||
);
|
if (!color) {
|
||||||
|
// random dark color
|
||||||
|
color =
|
||||||
|
'hsla(' + Math.floor(Math.random() * 360) + ', 70%, 30%, 1)';
|
||||||
|
colors.set(key, color);
|
||||||
|
}
|
||||||
|
|
||||||
|
measurement.labels[index].color = color;
|
||||||
|
measurement.isSRText = true;
|
||||||
|
measurement.labels[index].visible = true;
|
||||||
|
|
||||||
|
imageDisplaySet.SRLabels.push({
|
||||||
|
ReferencedSOPInstanceUID:
|
||||||
|
coord.ReferencedSOPSequence.ReferencedSOPInstanceUID,
|
||||||
|
labels: measurement.labels[index],
|
||||||
|
});
|
||||||
|
|
||||||
|
if (index === 0) {
|
||||||
|
addMeasurement(
|
||||||
|
measurement,
|
||||||
|
imageId,
|
||||||
|
imageMetadata,
|
||||||
|
imageDisplaySet.displaySetInstanceUID
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
addMeasurement(
|
||||||
|
measurement,
|
||||||
|
imageId,
|
||||||
|
imageMetadata,
|
||||||
|
imageDisplaySet.displaySetInstanceUID
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -26,8 +26,10 @@ export default function addMeasurement(
|
|||||||
|
|
||||||
const measurementData = {
|
const measurementData = {
|
||||||
TrackingUniqueIdentifier: measurement.TrackingUniqueIdentifier,
|
TrackingUniqueIdentifier: measurement.TrackingUniqueIdentifier,
|
||||||
|
TrackingIdentifier: measurement.TrackingIdentifier,
|
||||||
renderableData: {},
|
renderableData: {},
|
||||||
labels: measurement.labels,
|
labels: measurement.labels,
|
||||||
|
isSRText: measurement.isSRText,
|
||||||
};
|
};
|
||||||
|
|
||||||
measurement.coords.forEach(coord => {
|
measurement.coords.forEach(coord => {
|
||||||
|
|||||||
@@ -1,22 +1,14 @@
|
|||||||
const getCoordsFromSCOORDOrSCOORD3D = (graphicItem, displaySet) => {
|
const getCoordsFromSCOORDOrSCOORD3D = graphicItem => {
|
||||||
const { ValueType, RelationshipType, GraphicType, GraphicData } = graphicItem;
|
const { ValueType, GraphicType, GraphicData } = graphicItem;
|
||||||
|
|
||||||
// if (RelationshipType !== RELATIONSHIP_TYPE.INFERRED_FROM) {
|
|
||||||
// console.warn(
|
|
||||||
// `Relationshiptype === ${RelationshipType}. Cannot deal with NON TID-1400 SCOORD group with RelationshipType !== "INFERRED FROM."`
|
|
||||||
// );
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
|
|
||||||
const coords = { ValueType, GraphicType, GraphicData };
|
const coords = { ValueType, GraphicType, GraphicData };
|
||||||
|
|
||||||
// ContentSequence has length of 1 as RelationshipType === 'INFERRED FROM'
|
|
||||||
if (ValueType === 'SCOORD') {
|
if (ValueType === 'SCOORD') {
|
||||||
const { ReferencedSOPSequence } = graphicItem.ContentSequence;
|
const { ReferencedSOPSequence } = graphicItem.ContentSequence;
|
||||||
coords.ReferencedSOPSequence = ReferencedSOPSequence;
|
coords.ReferencedSOPSequence = ReferencedSOPSequence;
|
||||||
} else if (ValueType === 'SCOORD3D') {
|
} else if (ValueType === 'SCOORD3D') {
|
||||||
if (graphicItem.ReferencedFrameOfReferenceUID) {
|
if (graphicItem.ReferencedFrameOfReferenceUID) {
|
||||||
coords.ReferencedFrameOfReferenceSequence = graphicItem.ReferencedFrameOfReferenceUID;
|
coords.ReferencedFrameOfReferenceSequence =
|
||||||
|
graphicItem.ReferencedFrameOfReferenceUID;
|
||||||
} else if (graphicItem.ContentSequence) {
|
} else if (graphicItem.ContentSequence) {
|
||||||
const {
|
const {
|
||||||
ReferencedFrameOfReferenceSequence,
|
ReferencedFrameOfReferenceSequence,
|
||||||
|
|||||||
@@ -3,10 +3,7 @@ import getSequenceAsArray from './getSequenceAsArray';
|
|||||||
import getMergedContentSequencesByTrackingUniqueIdentifiers from './getMergedContentSequencesByTrackingUniqueIdentifiers';
|
import getMergedContentSequencesByTrackingUniqueIdentifiers from './getMergedContentSequencesByTrackingUniqueIdentifiers';
|
||||||
import processMeasurement from './processMeasurement';
|
import processMeasurement from './processMeasurement';
|
||||||
|
|
||||||
const getMeasurements = (
|
const getMeasurements = ImagingMeasurementReportContentSequence => {
|
||||||
ImagingMeasurementReportContentSequence,
|
|
||||||
displaySet
|
|
||||||
) => {
|
|
||||||
const ImagingMeasurements = ImagingMeasurementReportContentSequence.find(
|
const ImagingMeasurements = ImagingMeasurementReportContentSequence.find(
|
||||||
item =>
|
item =>
|
||||||
item.ConceptNameCodeSequence.CodeValue ===
|
item.ConceptNameCodeSequence.CodeValue ===
|
||||||
@@ -21,25 +18,19 @@ const getMeasurements = (
|
|||||||
CodeNameCodeSequenceValues.MeasurementGroup
|
CodeNameCodeSequenceValues.MeasurementGroup
|
||||||
);
|
);
|
||||||
|
|
||||||
const mergedContentSequencesByTrackingUniqueIdentifiers = getMergedContentSequencesByTrackingUniqueIdentifiers(
|
/* const mergedContentSequencesByTrackingUniqueIdentifiers = getMergedContentSequencesByTrackingUniqueIdentifiers(
|
||||||
MeasurementGroups
|
MeasurementGroups
|
||||||
);
|
);*/
|
||||||
|
|
||||||
let measurements = [];
|
let measurements = [];
|
||||||
|
|
||||||
Object.keys(mergedContentSequencesByTrackingUniqueIdentifiers).forEach(
|
MeasurementGroups.forEach(MeasurementGroup => {
|
||||||
trackingUniqueIdentifier => {
|
const contentSequence = MeasurementGroup.ContentSequence;
|
||||||
const mergedContentSequence =
|
const measurement = processMeasurement(contentSequence);
|
||||||
mergedContentSequencesByTrackingUniqueIdentifiers[
|
if (measurement) {
|
||||||
trackingUniqueIdentifier
|
measurements.push(measurement);
|
||||||
];
|
|
||||||
|
|
||||||
const measurement = processMeasurement(mergedContentSequence, displaySet);
|
|
||||||
if (measurement) {
|
|
||||||
measurements.push(measurement);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
);
|
});
|
||||||
|
|
||||||
return measurements;
|
return measurements;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,43 +0,0 @@
|
|||||||
import getSequenceAsArray from './getSequenceAsArray';
|
|
||||||
import { CodeNameCodeSequenceValues } from '../enums';
|
|
||||||
|
|
||||||
const getReferencedImagesList = ImagingMeasurementReportContentSequence => {
|
|
||||||
const referencedImages = [];
|
|
||||||
|
|
||||||
const ImageLibrary = ImagingMeasurementReportContentSequence.find(
|
|
||||||
item =>
|
|
||||||
item.ConceptNameCodeSequence.CodeValue ===
|
|
||||||
CodeNameCodeSequenceValues.ImageLibrary
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!ImageLibrary.ContentSequence) {
|
|
||||||
return referencedImages;
|
|
||||||
}
|
|
||||||
|
|
||||||
const ImageLibraryGroup = getSequenceAsArray(
|
|
||||||
ImageLibrary.ContentSequence
|
|
||||||
).find(
|
|
||||||
item =>
|
|
||||||
item.ConceptNameCodeSequence.CodeValue ===
|
|
||||||
CodeNameCodeSequenceValues.ImageLibraryGroup
|
|
||||||
);
|
|
||||||
|
|
||||||
getSequenceAsArray(ImageLibraryGroup.ContentSequence).forEach(item => {
|
|
||||||
const { ReferencedSOPSequence } = item;
|
|
||||||
if (ReferencedSOPSequence) {
|
|
||||||
const {
|
|
||||||
ReferencedSOPClassUID,
|
|
||||||
ReferencedSOPInstanceUID,
|
|
||||||
} = ReferencedSOPSequence;
|
|
||||||
|
|
||||||
referencedImages.push({
|
|
||||||
ReferencedSOPClassUID,
|
|
||||||
ReferencedSOPInstanceUID,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return referencedImages;
|
|
||||||
};
|
|
||||||
|
|
||||||
export default getReferencedImagesList;
|
|
||||||
@@ -57,6 +57,10 @@ const getRenderableData = (
|
|||||||
|
|
||||||
// https://dicom.innolitics.com/ciods/procedure-log/sr-document-content/00700023
|
// https://dicom.innolitics.com/ciods/procedure-log/sr-document-content/00700023
|
||||||
switch (GraphicType) {
|
switch (GraphicType) {
|
||||||
|
case SCOORD_TYPES.TEXT:
|
||||||
|
renderableData = [{ x: GraphicData[0], y: GraphicData[1] }];
|
||||||
|
|
||||||
|
break;
|
||||||
case SCOORD_TYPES.POINT:
|
case SCOORD_TYPES.POINT:
|
||||||
renderableData = [];
|
renderableData = [];
|
||||||
|
|
||||||
|
|||||||
@@ -1,16 +1,16 @@
|
|||||||
import processTID1410Measurement from './processTID1410Measurement';
|
import processTID1410Measurement from './processTID1410Measurement';
|
||||||
import processNonGeometricallyDefinedMeasurement from './processNonGeometricallyDefinedMeasurement';
|
import processNonGeometricallyDefinedMeasurement from './processNonGeometricallyDefinedMeasurement';
|
||||||
|
|
||||||
const processMeasurement = (mergedContentSequence, displaySet) => {
|
const processMeasurement = contentSequence => {
|
||||||
if (
|
if (
|
||||||
mergedContentSequence.some(
|
contentSequence.some(
|
||||||
group => group.ValueType === 'SCOORD' || group.ValueType === 'SCOORD3D'
|
group => group.ValueType === 'SCOORD' || group.ValueType === 'SCOORD3D'
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
return processTID1410Measurement(mergedContentSequence, displaySet);
|
return processTID1410Measurement(contentSequence);
|
||||||
}
|
}
|
||||||
|
|
||||||
return processNonGeometricallyDefinedMeasurement(mergedContentSequence);
|
return processNonGeometricallyDefinedMeasurement(contentSequence);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default processMeasurement;
|
export default processMeasurement;
|
||||||
+46
-6
@@ -1,31 +1,40 @@
|
|||||||
import getLabelFromMeasuredValueSequence from './getLabelFromMeasuredValueSequence';
|
import getLabelFromMeasuredValueSequence from './getLabelFromMeasuredValueSequence';
|
||||||
import getCoordsFromSCOORDOrSCOORD3D from './getCoordsFromSCOORDOrSCOORD3D';
|
import getCoordsFromSCOORDOrSCOORD3D from './getCoordsFromSCOORDOrSCOORD3D';
|
||||||
import { CodeNameCodeSequenceValues, CodingSchemeDesignators } from '../enums';
|
import { CodeNameCodeSequenceValues, CodingSchemeDesignators } from '../enums';
|
||||||
|
import SCOORD_TYPES from '../constants/scoordTypes';
|
||||||
|
|
||||||
const CORNERSTONE_FREETEXT_CODE_VALUE = 'CORNERSTONEFREETEXT';
|
const CORNERSTONE_FREETEXT_CODE_VALUE = 'CORNERSTONEFREETEXT';
|
||||||
|
|
||||||
const processNonGeometricallyDefinedMeasurement = mergedContentSequence => {
|
const processNonGeometricallyDefinedMeasurement = contentSequence => {
|
||||||
const NUMContentItems = mergedContentSequence.filter(
|
const NUMContentItems = contentSequence.filter(
|
||||||
group => group.ValueType === 'NUM'
|
group => group.ValueType === 'NUM'
|
||||||
);
|
);
|
||||||
|
|
||||||
const UIDREFContentItem = mergedContentSequence.find(
|
const CODEContentItems = contentSequence.filter(
|
||||||
|
group => group.ValueType === 'CODE'
|
||||||
|
);
|
||||||
|
|
||||||
|
const UIDREFContentItem = contentSequence.find(
|
||||||
group => group.ValueType === 'UIDREF'
|
group => group.ValueType === 'UIDREF'
|
||||||
);
|
);
|
||||||
|
|
||||||
const TrackingIdentifierContentItem = mergedContentSequence.find(
|
const IMAGEContentItem = contentSequence.find(
|
||||||
|
group => group.ValueType === 'IMAGE'
|
||||||
|
);
|
||||||
|
|
||||||
|
const TrackingIdentifierContentItem = contentSequence.find(
|
||||||
item =>
|
item =>
|
||||||
item.ConceptNameCodeSequence.CodeValue ===
|
item.ConceptNameCodeSequence.CodeValue ===
|
||||||
CodeNameCodeSequenceValues.TrackingIdentifier
|
CodeNameCodeSequenceValues.TrackingIdentifier
|
||||||
);
|
);
|
||||||
|
|
||||||
const Finding = mergedContentSequence.find(
|
const Finding = contentSequence.find(
|
||||||
item =>
|
item =>
|
||||||
item.ConceptNameCodeSequence.CodeValue ===
|
item.ConceptNameCodeSequence.CodeValue ===
|
||||||
CodeNameCodeSequenceValues.Finding
|
CodeNameCodeSequenceValues.Finding
|
||||||
);
|
);
|
||||||
|
|
||||||
const FindingSites = mergedContentSequence.filter(
|
const FindingSites = contentSequence.filter(
|
||||||
item =>
|
item =>
|
||||||
item.ConceptNameCodeSequence.CodingSchemeDesignator ===
|
item.ConceptNameCodeSequence.CodingSchemeDesignator ===
|
||||||
CodingSchemeDesignators.SRT &&
|
CodingSchemeDesignators.SRT &&
|
||||||
@@ -111,6 +120,37 @@ const processNonGeometricallyDefinedMeasurement = mergedContentSequence => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (NUMContentItems.length === 0 && IMAGEContentItem) {
|
||||||
|
CODEContentItems.forEach(item => {
|
||||||
|
const { ConceptCodeSequence, ConceptNameCodeSequence } = item;
|
||||||
|
|
||||||
|
if (!ConceptCodeSequence || !ConceptNameCodeSequence) {
|
||||||
|
console.warn(`Graphic missing, skipping annotation.`);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const GraphicData = [0, 0];
|
||||||
|
const GraphicType = SCOORD_TYPES.TEXT;
|
||||||
|
const { ValueType } = item;
|
||||||
|
const ReferencedSOPSequence = IMAGEContentItem.ReferencedSOPSequence;
|
||||||
|
const coord = {
|
||||||
|
ValueType,
|
||||||
|
GraphicType,
|
||||||
|
GraphicData,
|
||||||
|
ReferencedSOPSequence,
|
||||||
|
};
|
||||||
|
measurement.coords.push(coord);
|
||||||
|
measurement.labels.push({
|
||||||
|
label: ConceptNameCodeSequence.CodeMeaning,
|
||||||
|
labelCodingSchemeDesignator:
|
||||||
|
ConceptNameCodeSequence.CodingSchemeDesignator,
|
||||||
|
value: ConceptCodeSequence.CodeMeaning,
|
||||||
|
valueCodingSchemeDesignator: ConceptCodeSequence.CodingSchemeDesignator,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return measurement;
|
return measurement;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -5,14 +5,14 @@ import { RELATIONSHIP_TYPE, CodeNameCodeSequenceValues } from '../enums';
|
|||||||
/**
|
/**
|
||||||
* TID 1410 Planar ROI Measurements and Qualitative Evaluations.
|
* TID 1410 Planar ROI Measurements and Qualitative Evaluations.
|
||||||
*
|
*
|
||||||
* @param {*} mergedContentSequence
|
* @param {*} contentSequence
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
const processTID1410Measurement = (mergedContentSequence, displaySet) => {
|
const processTID1410Measurement = contentSequence => {
|
||||||
// Need to deal with TID 1410 style measurements, which will have a SCOORD or SCOORD3D at the top level,
|
// Need to deal with TID 1410 style measurements, which will have a SCOORD or SCOORD3D at the top level,
|
||||||
// And non-geometric representations where each NUM has "INFERRED FROM" SCOORD/SCOORD3D
|
// And non-geometric representations where each NUM has "INFERRED FROM" SCOORD/SCOORD3D
|
||||||
|
|
||||||
const graphicItem = mergedContentSequence.find(
|
const graphicItem = contentSequence.find(
|
||||||
group => group.ValueType === 'SCOORD' || group.ValueType === 'SCOORD3D'
|
group => group.ValueType === 'SCOORD' || group.ValueType === 'SCOORD3D'
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -23,24 +23,24 @@ const processTID1410Measurement = (mergedContentSequence, displaySet) => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const UIDREFContentItem = mergedContentSequence.find(
|
const UIDREFContentItem = contentSequence.find(
|
||||||
group => group.ValueType === 'UIDREF'
|
group => group.ValueType === 'UIDREF'
|
||||||
);
|
);
|
||||||
|
|
||||||
const TrackingIdentifierContentItem = mergedContentSequence.find(
|
const TrackingIdentifierContentItem = contentSequence.find(
|
||||||
item =>
|
item =>
|
||||||
item.ConceptNameCodeSequence.CodeValue ===
|
item.ConceptNameCodeSequence.CodeValue ===
|
||||||
CodeNameCodeSequenceValues.TrackingIdentifier
|
CodeNameCodeSequenceValues.TrackingIdentifier
|
||||||
);
|
);
|
||||||
|
|
||||||
const NUMContentItems = mergedContentSequence.filter(
|
const NUMContentItems = contentSequence.filter(
|
||||||
group => group.ValueType === 'NUM'
|
group => group.ValueType === 'NUM'
|
||||||
);
|
);
|
||||||
|
|
||||||
const measurement = {
|
const measurement = {
|
||||||
loaded: false,
|
loaded: false,
|
||||||
labels: [],
|
labels: [],
|
||||||
coords: [getCoordsFromSCOORDOrSCOORD3D(graphicItem, displaySet)],
|
coords: [getCoordsFromSCOORDOrSCOORD3D(graphicItem)],
|
||||||
TrackingUniqueIdentifier: UIDREFContentItem.UID,
|
TrackingUniqueIdentifier: UIDREFContentItem.UID,
|
||||||
TrackingIdentifier: TrackingIdentifierContentItem.TextValue,
|
TrackingIdentifier: TrackingIdentifierContentItem.TextValue,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -21,16 +21,32 @@ const parseDicomStructuredReport = (
|
|||||||
displaySets,
|
displaySets,
|
||||||
external
|
external
|
||||||
) => {
|
) => {
|
||||||
if (external && external.servicesManager) {
|
|
||||||
parseSCOORD3D({ servicesManager: external.servicesManager, displaySets });
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the dicom data as an Object
|
// Get the dicom data as an Object
|
||||||
const dicomData = dcmjs.data.DicomMessage.readFile(part10SRArrayBuffer);
|
const dicomData = dcmjs.data.DicomMessage.readFile(part10SRArrayBuffer);
|
||||||
const dataset = dcmjs.data.DicomMetaDictionary.naturalizeDataset(
|
const dataset = dcmjs.data.DicomMetaDictionary.naturalizeDataset(
|
||||||
dicomData.dict
|
dicomData.dict
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const {
|
||||||
|
LoggerService,
|
||||||
|
UINotificationService,
|
||||||
|
} = external.servicesManager.services;
|
||||||
|
if (external && external.servicesManager) {
|
||||||
|
try {
|
||||||
|
parseSCOORD3D({ servicesManager: external.servicesManager, displaySets });
|
||||||
|
} catch (error) {
|
||||||
|
const seriesDescription = dataset.SeriesDescription || '';
|
||||||
|
LoggerService.error({ error, message: error.message });
|
||||||
|
UINotificationService.show({
|
||||||
|
title: `Failed to parse ${seriesDescription} SR display set`,
|
||||||
|
message: error.message,
|
||||||
|
type: 'error',
|
||||||
|
autoClose: false,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const { MeasurementReport } = dcmjs.adapters.Cornerstone;
|
const { MeasurementReport } = dcmjs.adapters.Cornerstone;
|
||||||
|
|
||||||
let storedMeasurementByToolType;
|
let storedMeasurementByToolType;
|
||||||
@@ -38,11 +54,12 @@ const parseDicomStructuredReport = (
|
|||||||
storedMeasurementByToolType = MeasurementReport.generateToolState(dataset);
|
storedMeasurementByToolType = MeasurementReport.generateToolState(dataset);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const seriesDescription = dataset.SeriesDescription || '';
|
const seriesDescription = dataset.SeriesDescription || '';
|
||||||
LogManager.publish(LogManager.EVENTS.OnLog, {
|
LoggerService.error({ error, message: error.message });
|
||||||
|
UINotificationService.show({
|
||||||
title: `Failed to parse ${seriesDescription} measurement report`,
|
title: `Failed to parse ${seriesDescription} measurement report`,
|
||||||
type: 'warning',
|
message: error.message,
|
||||||
message: error.message || '',
|
type: 'error',
|
||||||
notify: true,
|
autoClose: false,
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -67,12 +67,13 @@ export class HotkeysManager {
|
|||||||
UINotificationService,
|
UINotificationService,
|
||||||
LoggerService,
|
LoggerService,
|
||||||
} = this._servicesManager.services;
|
} = this._servicesManager.services;
|
||||||
const message = 'Erro while setting hotkeys';
|
const message = 'Error while setting hotkeys';
|
||||||
LoggerService.error({ error, message });
|
LoggerService.error({ error, message });
|
||||||
UINotificationService.show({
|
UINotificationService.show({
|
||||||
title: 'Hotkeys Manager',
|
title: 'Hotkeys Manager',
|
||||||
message,
|
message,
|
||||||
type: 'error',
|
type: 'error',
|
||||||
|
autoClose: false,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -401,7 +401,7 @@ class StackLoadingListener extends BaseLoadingListener {
|
|||||||
const totalFramesCount = this.stack.imageIds.length;
|
const totalFramesCount = this.stack.imageIds.length;
|
||||||
const loadedFramesCount = this.loadedCount;
|
const loadedFramesCount = this.loadedCount;
|
||||||
const loadingFramesCount = totalFramesCount - loadedFramesCount;
|
const loadingFramesCount = totalFramesCount - loadedFramesCount;
|
||||||
const percentComplete = Math.round(
|
const percentComplete = Math.ceil(
|
||||||
(loadedFramesCount / totalFramesCount) * 100
|
(loadedFramesCount / totalFramesCount) * 100
|
||||||
);
|
);
|
||||||
const progressId = this._getProgressId();
|
const progressId = this._getProgressId();
|
||||||
@@ -469,8 +469,8 @@ class StudyLoadingListener {
|
|||||||
|
|
||||||
// TODO: Make this work for plugins
|
// TODO: Make this work for plugins
|
||||||
if (!stack) {
|
if (!stack) {
|
||||||
console.warn('Skipping adding displaySet to StudyLoadingListener');
|
//console.warn('Skipping adding displaySet to StudyLoadingListener');
|
||||||
console.warn(displaySet);
|
//console.warn(displaySet);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,12 +3,12 @@ import DICOMWeb from './../../DICOMWeb';
|
|||||||
import ImageSet from './../ImageSet';
|
import ImageSet from './../ImageSet';
|
||||||
import { InstanceMetadata } from './InstanceMetadata';
|
import { InstanceMetadata } from './InstanceMetadata';
|
||||||
import { Metadata } from './Metadata';
|
import { Metadata } from './Metadata';
|
||||||
import OHIFError from '../OHIFError';
|
|
||||||
import { SeriesMetadata } from './SeriesMetadata';
|
import { SeriesMetadata } from './SeriesMetadata';
|
||||||
// - createStacks
|
// - createStacks
|
||||||
import { api } from 'dicomweb-client';
|
import { api } from 'dicomweb-client';
|
||||||
// - createStacks
|
// - createStacks
|
||||||
import { isImage } from '../../utils/isImage';
|
import { isImage } from '../../utils/isImage';
|
||||||
|
import { naturalizeSOPClassUID } from '../../utils/naturalizeSOPClassUID';
|
||||||
import {
|
import {
|
||||||
isDisplaySetReconstructable,
|
isDisplaySetReconstructable,
|
||||||
isSpacingUniform,
|
isSpacingUniform,
|
||||||
@@ -169,8 +169,12 @@ class StudyMetadata extends Metadata {
|
|||||||
!isImage(instance.getTagValue('SOPClassUID')) &&
|
!isImage(instance.getTagValue('SOPClassUID')) &&
|
||||||
!instance.getTagValue('Rows')
|
!instance.getTagValue('Rows')
|
||||||
) {
|
) {
|
||||||
// we set an empty display and we add a isModalitySupported variable to
|
// we set an empty display and we add a isSOPClassUIDSupported variable to
|
||||||
// print a warning that the modality is not supported in the thumbnail.
|
// print a warning that the series is not supported in the thumbnail.
|
||||||
|
// SOPClassUIDNaturalized is human readable name, since for non image series,
|
||||||
|
// we could have a mismatch between the SOPClassUID and the Modality.
|
||||||
|
// For example, in the Parametric map IOD Modality is expected to match
|
||||||
|
// the value for the series used to generate Parametric map, and there is no "PM" modality.
|
||||||
const displaySet = new ImageSet([]);
|
const displaySet = new ImageSet([]);
|
||||||
const seriesData = series.getData();
|
const seriesData = series.getData();
|
||||||
displaySet.setAttributes({
|
displaySet.setAttributes({
|
||||||
@@ -188,7 +192,10 @@ class StudyMetadata extends Metadata {
|
|||||||
InstanceNumber: instance.getTagValue('InstanceNumber'), // Include the instance number
|
InstanceNumber: instance.getTagValue('InstanceNumber'), // Include the instance number
|
||||||
AcquisitionDatetime: instance.getTagValue('AcquisitionDateTime'), // Include the acquisition datetime
|
AcquisitionDatetime: instance.getTagValue('AcquisitionDateTime'), // Include the acquisition datetime
|
||||||
isReconstructable: false,
|
isReconstructable: false,
|
||||||
isModalitySupported: false,
|
isSOPClassUIDSupported: false,
|
||||||
|
SOPClassUIDNaturalized: naturalizeSOPClassUID(
|
||||||
|
instance.getTagValue('SOPClassUID')
|
||||||
|
),
|
||||||
metadata: instance.getData().metadata,
|
metadata: instance.getData().metadata,
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -268,6 +275,8 @@ class StudyMetadata extends Metadata {
|
|||||||
|
|
||||||
const otherDisplaySets = allDisplaySets.filter(
|
const otherDisplaySets = allDisplaySets.filter(
|
||||||
ds =>
|
ds =>
|
||||||
|
ds &&
|
||||||
|
derivatedDisplaySet &&
|
||||||
ds.displaySetInstanceUID !== derivatedDisplaySet.displaySetInstanceUID
|
ds.displaySetInstanceUID !== derivatedDisplaySet.displaySetInstanceUID
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -333,10 +342,38 @@ class StudyMetadata extends Metadata {
|
|||||||
if (referencedSeriesInstanceUID) {
|
if (referencedSeriesInstanceUID) {
|
||||||
filteredDerivedDisplaySets = filteredDerivedDisplaySets.filter(
|
filteredDerivedDisplaySets = filteredDerivedDisplaySets.filter(
|
||||||
displaySet => {
|
displaySet => {
|
||||||
return (
|
const referencedDisplaySet = StudyMetadata.getReferencedDisplaySet(
|
||||||
StudyMetadata.getReferencedDisplaySet(displaySet, [this])
|
displaySet,
|
||||||
.SeriesInstanceUID === referencedSeriesInstanceUID
|
[this]
|
||||||
);
|
);
|
||||||
|
if (referencedDisplaySet) {
|
||||||
|
return (
|
||||||
|
referencedDisplaySet.SeriesInstanceUID ===
|
||||||
|
referencedSeriesInstanceUID
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
if (
|
||||||
|
!displaySet.referencedDisplaySets ||
|
||||||
|
displaySet.referencedDisplaySets.length === 0
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const filteredReferencedDisplaySets = displaySet.referencedDisplaySets.filter(
|
||||||
|
referencedDisplaySet =>
|
||||||
|
referencedDisplaySet.SeriesInstanceUID ===
|
||||||
|
referencedSeriesInstanceUID
|
||||||
|
);
|
||||||
|
|
||||||
|
if (
|
||||||
|
filteredReferencedDisplaySets &&
|
||||||
|
filteredReferencedDisplaySets.length !== 0
|
||||||
|
) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -923,7 +960,7 @@ const makeDisplaySet = (series, instances) => {
|
|||||||
: displayReconstructableInfo.reconstructionIssues;
|
: displayReconstructableInfo.reconstructionIssues;
|
||||||
}
|
}
|
||||||
|
|
||||||
imageSet.isModalitySupported = true;
|
imageSet.isSOPClassUIDSupported = true;
|
||||||
|
|
||||||
return imageSet;
|
return imageSet;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -6,7 +6,8 @@ export const dicomSRDisplayTool = {
|
|||||||
options: {
|
options: {
|
||||||
measurementTable: {
|
measurementTable: {
|
||||||
displayFunction: data => {
|
displayFunction: data => {
|
||||||
return `(SR) ${data.lesionNamingNumber ||
|
return `(SR) ${data.TrackingIdentifier ||
|
||||||
|
data.lesionNamingNumber ||
|
||||||
data.measurementNumber ||
|
data.measurementNumber ||
|
||||||
data.text ||
|
data.text ||
|
||||||
''}`;
|
''}`;
|
||||||
|
|||||||
@@ -420,6 +420,10 @@ class MeasurementService {
|
|||||||
throw new Error('No source definition provided.');
|
throw new Error('No source definition provided.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (definition == 'stack' || definition == 'stackPrefetch') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const sourceInfo = this._getSourceInfo(source);
|
const sourceInfo = this._getSourceInfo(source);
|
||||||
|
|
||||||
if (!this._sourceHasMappings(source)) {
|
if (!this._sourceHasMappings(source)) {
|
||||||
|
|||||||
@@ -19,17 +19,4 @@ describe('retrieveStudyMetadata.js', () => {
|
|||||||
|
|
||||||
expect(callWithNoStudyInstanceUID).toThrow(Error);
|
expect(callWithNoStudyInstanceUID).toThrow(Error);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('caches and returns the same promise for identical studyInstanceUIDs', () => {
|
|
||||||
const firstPromise = retrieveStudyMetadata(
|
|
||||||
fakeDicomWebServer,
|
|
||||||
'fake-study-instance-uid'
|
|
||||||
);
|
|
||||||
const secondPromise = retrieveStudyMetadata(
|
|
||||||
fakeDicomWebServer,
|
|
||||||
'fake-study-instance-uid'
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(firstPromise).toBe(secondPromise);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
@@ -21,16 +21,18 @@ const getReferencedSeriesSequence = instance => {
|
|||||||
const referencedInstanceSequenceRaw = referencedSeries['0008114A'];
|
const referencedInstanceSequenceRaw = referencedSeries['0008114A'];
|
||||||
const referencedInstanceSequence = [];
|
const referencedInstanceSequence = [];
|
||||||
|
|
||||||
referencedInstanceSequenceRaw.Value.forEach(referencedInstance => {
|
if (referencedInstanceSequenceRaw) {
|
||||||
referencedInstanceSequence.push({
|
referencedInstanceSequenceRaw.Value.forEach(referencedInstance => {
|
||||||
referencedSOPClassUID: DICOMWeb.getString(
|
referencedInstanceSequence.push({
|
||||||
referencedInstance['00081150']
|
referencedSOPClassUID: DICOMWeb.getString(
|
||||||
),
|
referencedInstance['00081150']
|
||||||
referencedSOPInstanceUID: DICOMWeb.getString(
|
),
|
||||||
referencedInstance['00081155']
|
referencedSOPInstanceUID: DICOMWeb.getString(
|
||||||
),
|
referencedInstance['00081155']
|
||||||
|
),
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
}
|
||||||
|
|
||||||
referencedSeriesSequence.push({
|
referencedSeriesSequence.push({
|
||||||
referencedSeriesInstanceUID,
|
referencedSeriesInstanceUID,
|
||||||
|
|||||||
@@ -102,6 +102,35 @@ async function makeSOPInstance(server, study, instance) {
|
|||||||
SOPInstanceUID,
|
SOPInstanceUID,
|
||||||
} = naturalizedInstance;
|
} = naturalizedInstance;
|
||||||
|
|
||||||
|
const validate = string => {
|
||||||
|
let rgx = /[^.0-9]+/g;
|
||||||
|
return string.match(rgx);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (StudyInstanceUID === undefined || validate(StudyInstanceUID)) {
|
||||||
|
const error =
|
||||||
|
'makeSOPInstance: StudyInstanceUID is not conforming with the UID (DICOM UI VR) character repertoire, skipping SOPInstance.';
|
||||||
|
console.error(error);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (SeriesInstanceUID === undefined || validate(SeriesInstanceUID)) {
|
||||||
|
const error =
|
||||||
|
'makeSOPInstance: SeriesInstanceUID is not conforming with the UID (DICOM UI VR) character repertoire, skipping SOPInstance.';
|
||||||
|
console.error(error);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (SOPInstanceUID === undefined || validate(SOPInstanceUID)) {
|
||||||
|
const error =
|
||||||
|
'makeSOPInstance: SOPInstanceUID is not conforming with the UID (DICOM UI VR) character repertoire, skipping SOPInstance.';
|
||||||
|
console.error(error);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
let series = study.seriesMap[SeriesInstanceUID];
|
let series = study.seriesMap[SeriesInstanceUID];
|
||||||
|
|
||||||
if (!series) {
|
if (!series) {
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ function createAndAddStack(stackMap, study, displaySet, stackUpdatedCallbacks) {
|
|||||||
const imageIds = [];
|
const imageIds = [];
|
||||||
let imageId;
|
let imageId;
|
||||||
|
|
||||||
displaySet.images.forEach((instance, imageIndex) => {
|
images.forEach((instance, imageIndex) => {
|
||||||
const image = instance.getData();
|
const image = instance.getData();
|
||||||
const metaData = {
|
const metaData = {
|
||||||
instance: image, // in this context, instance will be the data of the InstanceMetadata object...
|
instance: image, // in this context, instance will be the data of the InstanceMetadata object...
|
||||||
@@ -35,7 +35,7 @@ function createAndAddStack(stackMap, study, displaySet, stackUpdatedCallbacks) {
|
|||||||
imageIndex: imageIndex + 1,
|
imageIndex: imageIndex + 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
const naturalizedInstance = instance.getData().metadata;
|
const naturalizedInstance = image.metadata;
|
||||||
const NumberOfFrames = naturalizedInstance.NumberOfFrames;
|
const NumberOfFrames = naturalizedInstance.NumberOfFrames;
|
||||||
|
|
||||||
if (NumberOfFrames > 1) {
|
if (NumberOfFrames > 1) {
|
||||||
|
|||||||
@@ -49,7 +49,12 @@ import studyMetadataManager from './studyMetadataManager';
|
|||||||
* @param {object} snackbar
|
* @param {object} snackbar
|
||||||
* @returns void
|
* @returns void
|
||||||
*/
|
*/
|
||||||
async function loadAndCacheDerivedDisplaySets(referencedDisplaySet, studies, logger, snackbar) {
|
async function loadAndCacheDerivedDisplaySets(
|
||||||
|
referencedDisplaySet,
|
||||||
|
studies,
|
||||||
|
logger,
|
||||||
|
snackbar
|
||||||
|
) {
|
||||||
const { StudyInstanceUID, SeriesInstanceUID } = referencedDisplaySet;
|
const { StudyInstanceUID, SeriesInstanceUID } = referencedDisplaySet;
|
||||||
const promises = [];
|
const promises = [];
|
||||||
const studyMetadata = studyMetadataManager.get(StudyInstanceUID);
|
const studyMetadata = studyMetadataManager.get(StudyInstanceUID);
|
||||||
@@ -80,60 +85,119 @@ async function loadAndCacheDerivedDisplaySets(referencedDisplaySet, studies, log
|
|||||||
});
|
});
|
||||||
|
|
||||||
// For each type, see if any are loaded, if not load the most recent.
|
// For each type, see if any are loaded, if not load the most recent.
|
||||||
await Promise.all(Object.keys(displaySetsPerModality).map(async (key) => {
|
await Promise.all(
|
||||||
const displaySets = displaySetsPerModality[key];
|
Object.keys(displaySetsPerModality).map(async key => {
|
||||||
|
const displaySets = displaySetsPerModality[key];
|
||||||
|
|
||||||
const isLoaded = displaySets.some(displaySet => displaySet.isLoaded);
|
const isLoaded = displaySets.some(displaySet => displaySet.isLoaded);
|
||||||
if (isLoaded) {
|
if (isLoaded) {
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
if (displaySets.some(displaySet => displaySet.loadError)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// find most recent and load it.
|
|
||||||
let recentDateTime = 0;
|
|
||||||
let recentDisplaySet = displaySets[0];
|
|
||||||
|
|
||||||
displaySets.forEach(displaySet => {
|
|
||||||
const dateTime = Number(
|
|
||||||
`${displaySet.SeriesDate}${displaySet.SeriesTime}`
|
|
||||||
);
|
|
||||||
if (dateTime > recentDateTime) {
|
|
||||||
recentDateTime = dateTime;
|
|
||||||
recentDisplaySet = displaySet;
|
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
|
||||||
try {
|
if (displaySets.some(displaySet => displaySet.loadError)) {
|
||||||
if (recentDisplaySet.hasOwnProperty('getSourceDisplaySet') &&
|
return;
|
||||||
typeof recentDisplaySet.getSourceDisplaySet === 'function') {
|
|
||||||
await recentDisplaySet.getSourceDisplaySet(studies);
|
|
||||||
} else {
|
|
||||||
await recentDisplaySet.load(referencedDisplaySet, studies);
|
|
||||||
}
|
}
|
||||||
} catch (error) {
|
|
||||||
recentDisplaySet.isLoaded = false;
|
// find most recent and load it.
|
||||||
recentDisplaySet.loadError = true;
|
let recentDateTime = 0;
|
||||||
logger.error({ error, message: error.message });
|
let recentDisplaySet = displaySets[0];
|
||||||
snackbar.show({
|
|
||||||
title: 'Error loading derived display set:',
|
displaySets.forEach(displaySet => {
|
||||||
message: error.message,
|
const dateTime = Number(
|
||||||
type: 'error',
|
`${displaySet.SeriesDate}${displaySet.SeriesTime}`
|
||||||
error,
|
);
|
||||||
autoClose: false,
|
if (dateTime > recentDateTime) {
|
||||||
|
recentDateTime = dateTime;
|
||||||
|
recentDisplaySet = displaySet;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
|
||||||
}));
|
try {
|
||||||
|
if (
|
||||||
|
recentDisplaySet.hasOwnProperty('getSourceDisplaySet') &&
|
||||||
|
typeof recentDisplaySet.getSourceDisplaySet === 'function'
|
||||||
|
) {
|
||||||
|
if (recentDisplaySet.Modality === 'SEG' && logger) {
|
||||||
|
const onDisplaySetLoadFailureHandler = error => {
|
||||||
|
const message =
|
||||||
|
error.message.includes('orthogonal') ||
|
||||||
|
error.message.includes('oblique')
|
||||||
|
? 'The segmentation has been detected as non coplanar,\
|
||||||
|
If you really think it is coplanar,\
|
||||||
|
please adjust the tolerance in the segmentation panel settings (at your own peril!)'
|
||||||
|
: error.message;
|
||||||
|
|
||||||
|
logger.error({ error, message });
|
||||||
|
snackbar.show({
|
||||||
|
title: 'DICOM Segmentation Loader',
|
||||||
|
message,
|
||||||
|
type: 'error',
|
||||||
|
autoClose: false,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
let activatedLabelmapIndex = -1;
|
||||||
|
while (activatedLabelmapIndex == -1) {
|
||||||
|
const {
|
||||||
|
referencedDisplaySet,
|
||||||
|
activatedLabelmapPromise,
|
||||||
|
} = await recentDisplaySet.getSourceDisplaySet(
|
||||||
|
studies,
|
||||||
|
true,
|
||||||
|
onDisplaySetLoadFailureHandler
|
||||||
|
);
|
||||||
|
|
||||||
|
activatedLabelmapIndex = await activatedLabelmapPromise;
|
||||||
|
const selectionFired = new CustomEvent(
|
||||||
|
'extensiondicomsegmentationsegselected',
|
||||||
|
{
|
||||||
|
detail: { activatedLabelmapIndex: activatedLabelmapIndex },
|
||||||
|
}
|
||||||
|
);
|
||||||
|
document.dispatchEvent(selectionFired);
|
||||||
|
|
||||||
|
const lastDateTime = Number(
|
||||||
|
`${recentDisplaySet.SeriesDate}${recentDisplaySet.SeriesTime}`
|
||||||
|
);
|
||||||
|
recentDateTime = 0;
|
||||||
|
displaySets.forEach(displaySet => {
|
||||||
|
const dateTime = Number(
|
||||||
|
`${displaySet.SeriesDate}${displaySet.SeriesTime}`
|
||||||
|
);
|
||||||
|
if (dateTime > recentDateTime && dateTime < lastDateTime) {
|
||||||
|
recentDateTime = dateTime;
|
||||||
|
recentDisplaySet = displaySet;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
await recentDisplaySet.getSourceDisplaySet(studies);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
await recentDisplaySet.load(referencedDisplaySet, studies);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
recentDisplaySet.isLoaded = false;
|
||||||
|
recentDisplaySet.loadError = true;
|
||||||
|
logger.error({ error, message: error.message });
|
||||||
|
snackbar.show({
|
||||||
|
title: 'Error loading derived display set:',
|
||||||
|
message: error.message,
|
||||||
|
type: 'error',
|
||||||
|
error,
|
||||||
|
autoClose: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* TODO: Improve the way we notify parts of the app
|
* TODO: Improve the way we notify parts of the app
|
||||||
* that depends on derived display sets to be loaded.
|
* that depends on derived display sets to be loaded.
|
||||||
* (Implement pubsub for better tracking of derived display sets)
|
* (Implement pubsub for better tracking of derived display sets)
|
||||||
*/
|
*/
|
||||||
const event = new CustomEvent('deriveddisplaysetsloadedandcached');
|
const event = new CustomEvent('deriveddisplaysetsloadedandcached');
|
||||||
document.dispatchEvent(event);
|
document.dispatchEvent(event);
|
||||||
};
|
}
|
||||||
|
|
||||||
export default loadAndCacheDerivedDisplaySets;
|
export default loadAndCacheDerivedDisplaySets;
|
||||||
@@ -0,0 +1,200 @@
|
|||||||
|
import { sopClassDictionary } from './sopClassDictionary';
|
||||||
|
import { isImage } from './isImage';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Naturalize SOP Class UID which do not have image data
|
||||||
|
* @param {string} SOPClassUID - SOP Class UID to be converted
|
||||||
|
* @returns {string} - human readable name
|
||||||
|
*/
|
||||||
|
export const naturalizeSOPClassUID = SOPClassUID => {
|
||||||
|
let naturalizedName = '';
|
||||||
|
if (!SOPClassUID) return naturalizedName;
|
||||||
|
if (!isImage) return naturalizedName;
|
||||||
|
|
||||||
|
if (sopClassDictionary.MRSpectroscopyStorage === SOPClassUID) {
|
||||||
|
naturalizedName = 'MRSpectroscopy';
|
||||||
|
} else if (sopClassDictionary.EnhancedUSVolumeStorage === SOPClassUID) {
|
||||||
|
naturalizedName = 'EnhancedUSVolume';
|
||||||
|
} else if (sopClassDictionary.Sop12LeadECGWaveformStorage === SOPClassUID) {
|
||||||
|
naturalizedName = 'Sop12LeadECGWaveform';
|
||||||
|
} else if (sopClassDictionary.GeneralECGWaveformStorage === SOPClassUID) {
|
||||||
|
naturalizedName = 'GeneralECGWaveform';
|
||||||
|
} else if (sopClassDictionary.AmbulatoryECGWaveformStorage === SOPClassUID) {
|
||||||
|
naturalizedName = 'AECAmbulatoryECGWaveformGW';
|
||||||
|
} else if (sopClassDictionary.HemodynamicWaveformStorage === SOPClassUID) {
|
||||||
|
naturalizedName = 'HemodynamicWaveform';
|
||||||
|
} else if (
|
||||||
|
sopClassDictionary.CardiacElectrophysiologyWaveformStorage === SOPClassUID
|
||||||
|
) {
|
||||||
|
naturalizedName = 'CardiacElectrophysiologyWaveform';
|
||||||
|
} else if (
|
||||||
|
sopClassDictionary.BasicVoiceAudioWaveformStorage === SOPClassUID
|
||||||
|
) {
|
||||||
|
naturalizedName = 'BasicVoiceAudioWaveform';
|
||||||
|
} else if (sopClassDictionary.GeneralAudioWaveformStorage === SOPClassUID) {
|
||||||
|
naturalizedName = 'GGeneralAudioWaveformAW';
|
||||||
|
} else if (sopClassDictionary.ArterialPulseWaveformStorage === SOPClassUID) {
|
||||||
|
naturalizedName = 'APArterialPulseWaveformW';
|
||||||
|
} else if (sopClassDictionary.RespiratoryWaveformStorage === SOPClassUID) {
|
||||||
|
naturalizedName = 'RespiratoryWaveform';
|
||||||
|
} else if (
|
||||||
|
sopClassDictionary.GrayscaleSoftcopyPresentationStateStorage === SOPClassUID
|
||||||
|
) {
|
||||||
|
naturalizedName = 'GrayscaleSoftcopyPresentationState';
|
||||||
|
} else if (
|
||||||
|
sopClassDictionary.ColorSoftcopyPresentationStateStorage === SOPClassUID
|
||||||
|
) {
|
||||||
|
naturalizedName = 'ColorSoftcopyPresentationState';
|
||||||
|
} else if (
|
||||||
|
sopClassDictionary.PseudoColorSoftcopyPresentationStateStorage ===
|
||||||
|
SOPClassUID
|
||||||
|
) {
|
||||||
|
naturalizedName = 'PseudoColorSoftcopyPresentationState';
|
||||||
|
} else if (
|
||||||
|
sopClassDictionary.BlendingSoftcopyPresentationStateStorage === SOPClassUID
|
||||||
|
) {
|
||||||
|
naturalizedName = 'BlendingSoftcopyPresentationState';
|
||||||
|
} else if (
|
||||||
|
sopClassDictionary.XAXRFGrayscaleSoftcopyPresentationStateStorage ===
|
||||||
|
SOPClassUID
|
||||||
|
) {
|
||||||
|
naturalizedName = 'XAXRFGrayscaleSoftcopyPresentationState';
|
||||||
|
} else if (sopClassDictionary.RawDataStorage === SOPClassUID) {
|
||||||
|
naturalizedName = 'RawData';
|
||||||
|
} else if (sopClassDictionary.SpatialRegistrationStorage === SOPClassUID) {
|
||||||
|
naturalizedName = 'SpatialRegistration';
|
||||||
|
} else if (sopClassDictionary.SpatialFiducialsStorage === SOPClassUID) {
|
||||||
|
naturalizedName = 'SpatialFiducials';
|
||||||
|
} else if (
|
||||||
|
sopClassDictionary.DeformableSpatialRegistrationStorage === SOPClassUID
|
||||||
|
) {
|
||||||
|
naturalizedName = 'DeformableSpatialRegistration';
|
||||||
|
} else if (sopClassDictionary.SegmentationStorage === SOPClassUID) {
|
||||||
|
naturalizedName = 'SEG';
|
||||||
|
} else if (sopClassDictionary.SurfaceSegmentationStorage === SOPClassUID) {
|
||||||
|
naturalizedName = 'SurfaceSEG';
|
||||||
|
} else if (sopClassDictionary.RealWorldValueMappingStorage === SOPClassUID) {
|
||||||
|
naturalizedName = 'RealWorldValueMapping';
|
||||||
|
} else if (sopClassDictionary.SurfaceScanMeshStorage === SOPClassUID) {
|
||||||
|
naturalizedName = 'SurfaceScanMesh';
|
||||||
|
} else if (sopClassDictionary.SurfaceScanPointCloudStorage === SOPClassUID) {
|
||||||
|
naturalizedName = 'SurfaceScanPointCloud';
|
||||||
|
} else if (
|
||||||
|
sopClassDictionary.StereometricRelationshipStorage === SOPClassUID
|
||||||
|
) {
|
||||||
|
naturalizedName = 'StereometricRelationship';
|
||||||
|
} else if (sopClassDictionary.LensometryMeasurementsStorage === SOPClassUID) {
|
||||||
|
naturalizedName = 'LensometryMeasurements';
|
||||||
|
} else if (
|
||||||
|
sopClassDictionary.AutorefractionMeasurementsStorage === SOPClassUID
|
||||||
|
) {
|
||||||
|
naturalizedName = 'AutorefractionMeasurements';
|
||||||
|
} else if (
|
||||||
|
sopClassDictionary.KeratometryMeasurementsStorage === SOPClassUID
|
||||||
|
) {
|
||||||
|
naturalizedName = 'KeratometryMeasurements';
|
||||||
|
} else if (
|
||||||
|
sopClassDictionary.SubjectiveRefractionMeasurementsStorage === SOPClassUID
|
||||||
|
) {
|
||||||
|
naturalizedName = 'SubjectiveRefractionMeasurements';
|
||||||
|
} else if (
|
||||||
|
sopClassDictionary.VisualAcuityMeasurementsStorage === SOPClassUID
|
||||||
|
) {
|
||||||
|
naturalizedName = 'VisualAcuityMeasurements';
|
||||||
|
} else if (
|
||||||
|
sopClassDictionary.SpectaclePrescriptionReportStorage === SOPClassUID
|
||||||
|
) {
|
||||||
|
naturalizedName = 'SpectaclePrescriptionReport';
|
||||||
|
} else if (
|
||||||
|
sopClassDictionary.OphthalmicAxialMeasurementsStorage === SOPClassUID
|
||||||
|
) {
|
||||||
|
naturalizedName = 'OphthalmicAxialMeasurements';
|
||||||
|
} else if (
|
||||||
|
sopClassDictionary.IntraocularLensCalculationsStorage === SOPClassUID
|
||||||
|
) {
|
||||||
|
naturalizedName = 'IntraocularLensCalculations';
|
||||||
|
} else if (
|
||||||
|
sopClassDictionary.MacularGridThicknessandVolumeReport === SOPClassUID
|
||||||
|
) {
|
||||||
|
naturalizedName = 'MacularGridThicknessandVolume';
|
||||||
|
} else if (
|
||||||
|
sopClassDictionary.OphthalmicVisualFieldStaticPerimetryMeasurementsStorage ===
|
||||||
|
SOPClassUID
|
||||||
|
) {
|
||||||
|
naturalizedName = 'OphthalmicVisualFieldStaticPerimetryMeasurements';
|
||||||
|
} else if (sopClassDictionary.OphthalmicThicknessMapStorage === SOPClassUID) {
|
||||||
|
naturalizedName = 'OphthalmicThicknessMap';
|
||||||
|
} else if (sopClassDictionary.CornealTopographyMapStorage === SOPClassUID) {
|
||||||
|
naturalizedName = 'CornealTopographyMap';
|
||||||
|
} else if (sopClassDictionary.BasicTextSR === SOPClassUID) {
|
||||||
|
naturalizedName = 'BasicTextSR';
|
||||||
|
} else if (sopClassDictionary.EnhancedSR === SOPClassUID) {
|
||||||
|
naturalizedName = 'EnhancedSR';
|
||||||
|
} else if (sopClassDictionary.ComprehensiveSR === SOPClassUID) {
|
||||||
|
naturalizedName = 'ComprehensiveSR';
|
||||||
|
} else if (sopClassDictionary.Comprehensive3DSR === SOPClassUID) {
|
||||||
|
naturalizedName = 'Comprehensive3DSR';
|
||||||
|
} else if (sopClassDictionary.ProcedureLog === SOPClassUID) {
|
||||||
|
naturalizedName = 'ProcedureLog';
|
||||||
|
} else if (sopClassDictionary.MammographyCADSR === SOPClassUID) {
|
||||||
|
naturalizedName = 'MammographyCADSR';
|
||||||
|
} else if (sopClassDictionary.KeyObjectSelection === SOPClassUID) {
|
||||||
|
naturalizedName = 'KeyObject';
|
||||||
|
} else if (sopClassDictionary.ChestCADSR === SOPClassUID) {
|
||||||
|
naturalizedName = 'ChestCADSR';
|
||||||
|
} else if (sopClassDictionary.XRayRadiationDoseSR === SOPClassUID) {
|
||||||
|
naturalizedName = 'XRayRadiationDoseSR';
|
||||||
|
} else if (
|
||||||
|
sopClassDictionary.RadiopharmaceuticalRadiationDoseSR === SOPClassUID
|
||||||
|
) {
|
||||||
|
naturalizedName = 'RadiopharmaceuticalRadiationDoseSR';
|
||||||
|
} else if (sopClassDictionary.ColonCADSR === SOPClassUID) {
|
||||||
|
naturalizedName = 'ColonCADSR';
|
||||||
|
} else if (
|
||||||
|
sopClassDictionary.ImplantationPlanSRDocumentStorage === SOPClassUID
|
||||||
|
) {
|
||||||
|
naturalizedName = 'ImplantationPlanSRDocument';
|
||||||
|
} else if (sopClassDictionary.EncapsulatedPDFStorage === SOPClassUID) {
|
||||||
|
naturalizedName = 'EncapsulatedPDF';
|
||||||
|
} else if (sopClassDictionary.EncapsulatedCDAStorage === SOPClassUID) {
|
||||||
|
naturalizedName = 'EncapsulatedCDA';
|
||||||
|
} else if (sopClassDictionary.BasicStructuredDisplayStorage === SOPClassUID) {
|
||||||
|
naturalizedName = 'BasicStructuredDisplay';
|
||||||
|
} else if (sopClassDictionary.RTDoseStorage === SOPClassUID) {
|
||||||
|
naturalizedName = 'RTDose';
|
||||||
|
} else if (sopClassDictionary.RTStructureSetStorage === SOPClassUID) {
|
||||||
|
naturalizedName = 'RTStructureSet';
|
||||||
|
} else if (sopClassDictionary.RTBeamsTreatmentRecordStorage === SOPClassUID) {
|
||||||
|
naturalizedName = 'RTBeamsTreatmentRecord';
|
||||||
|
} else if (sopClassDictionary.RTPlanStorage === SOPClassUID) {
|
||||||
|
naturalizedName = 'RTPlan';
|
||||||
|
} else if (
|
||||||
|
sopClassDictionary.RTBrachyTreatmentRecordStorage === SOPClassUID
|
||||||
|
) {
|
||||||
|
naturalizedName = 'RTBrachyTreatmentRecord';
|
||||||
|
} else if (
|
||||||
|
sopClassDictionary.RTTreatmentSummaryRecordStorage === SOPClassUID
|
||||||
|
) {
|
||||||
|
naturalizedName = 'RTTreatmentSummaryRecord';
|
||||||
|
} else if (sopClassDictionary.RTIonPlanStorage === SOPClassUID) {
|
||||||
|
naturalizedName = 'RTIonPlan';
|
||||||
|
} else if (
|
||||||
|
sopClassDictionary.RTIonBeamsTreatmentRecordStorage === SOPClassUID
|
||||||
|
) {
|
||||||
|
naturalizedName = 'RTIonBeamsTreatmentRecord';
|
||||||
|
} else if (
|
||||||
|
sopClassDictionary.RTBeamsDeliveryInstructionStorage === SOPClassUID
|
||||||
|
) {
|
||||||
|
naturalizedName = 'RTBeamsDeliveryInstruction';
|
||||||
|
} else if (sopClassDictionary.GenericImplantTemplateStorage === SOPClassUID) {
|
||||||
|
naturalizedName = 'GenericImplantTemplate';
|
||||||
|
} else if (
|
||||||
|
sopClassDictionary.ImplantAssemblyTemplateStorage === SOPClassUID
|
||||||
|
) {
|
||||||
|
naturalizedName = 'ImplantAssemblyTemplate';
|
||||||
|
} else if (sopClassDictionary.ImplantTemplateGroupStorage === SOPClassUID) {
|
||||||
|
naturalizedName = 'ImplantTemplateGroup';
|
||||||
|
}
|
||||||
|
|
||||||
|
return naturalizedName;
|
||||||
|
};
|
||||||
@@ -3,6 +3,38 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
## [1.10.11](https://github.com/OHIF/Viewers/compare/@ohif/ui@1.10.10...@ohif/ui@1.10.11) (2023-03-19)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/ui
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [1.10.10](https://github.com/OHIF/Viewers/compare/@ohif/ui@1.10.9...@ohif/ui@1.10.10) (2022-08-24)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/ui
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [1.10.9](https://github.com/OHIF/Viewers/compare/@ohif/ui@1.10.8...@ohif/ui@1.10.9) (2022-06-29)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/ui
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [1.10.8](https://github.com/OHIF/Viewers/compare/@ohif/ui@1.10.7...@ohif/ui@1.10.8) (2022-04-06)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/ui
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## [1.10.7](https://github.com/OHIF/Viewers/compare/@ohif/ui@1.10.6...@ohif/ui@1.10.7) (2022-03-14)
|
## [1.10.7](https://github.com/OHIF/Viewers/compare/@ohif/ui@1.10.6...@ohif/ui@1.10.7) (2022-03-14)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@ohif/ui",
|
"name": "@ohif/ui",
|
||||||
"version": "1.10.7",
|
"version": "1.10.11",
|
||||||
"description": "A set of React components for Medical Imaging Viewers",
|
"description": "A set of React components for Medical Imaging Viewers",
|
||||||
"author": "OHIF Contributors",
|
"author": "OHIF Contributors",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
@@ -45,7 +45,7 @@
|
|||||||
"i18next-browser-languagedetector": "^3.0.1",
|
"i18next-browser-languagedetector": "^3.0.1",
|
||||||
"lodash.debounce": "^4.0.8",
|
"lodash.debounce": "^4.0.8",
|
||||||
"lodash.isequal": "4.5.0",
|
"lodash.isequal": "4.5.0",
|
||||||
"moment": "2.24.0",
|
"moment": "2.29.4",
|
||||||
"prop-types": "15.6.2",
|
"prop-types": "15.6.2",
|
||||||
"prop-types-extra": "^1.1.1",
|
"prop-types-extra": "^1.1.1",
|
||||||
"react-dates": "21.2.1",
|
"react-dates": "21.2.1",
|
||||||
|
|||||||
@@ -9,6 +9,14 @@ import { TableListItem } from './../tableList/TableListItem.js';
|
|||||||
|
|
||||||
import './MeasurementTableItem.styl';
|
import './MeasurementTableItem.styl';
|
||||||
|
|
||||||
|
const ColoredCircle = ({ color }) => {
|
||||||
|
return <div className="item-color" style={{ backgroundColor: color }}></div>;
|
||||||
|
};
|
||||||
|
|
||||||
|
ColoredCircle.propTypes = {
|
||||||
|
color: PropTypes.string.isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
class MeasurementTableItem extends Component {
|
class MeasurementTableItem extends Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
measurementData: PropTypes.object.isRequired,
|
measurementData: PropTypes.object.isRequired,
|
||||||
@@ -21,6 +29,14 @@ class MeasurementTableItem extends Component {
|
|||||||
t: PropTypes.func,
|
t: PropTypes.func,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
this.state = {
|
||||||
|
collapsed: true,
|
||||||
|
visible: true,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {
|
const {
|
||||||
warningTitle = '',
|
warningTitle = '',
|
||||||
@@ -93,27 +109,115 @@ class MeasurementTableItem extends Component {
|
|||||||
actionButtons.push(deleteButton);
|
actionButtons.push(deleteButton);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
if (
|
||||||
<TableListItem
|
this.props.measurementData.isSRText === true &&
|
||||||
itemKey={this.props.measurementData.measurementNumber}
|
this.props.measurementData.labels &&
|
||||||
itemClass={`measurementItem ${this.props.itemClass} ${hasWarningClass}`}
|
this.props.measurementData.labels.length > 0
|
||||||
itemIndex={this.props.itemIndex}
|
) {
|
||||||
onItemClick={this.onItemClick}
|
return (
|
||||||
>
|
<React.Fragment>
|
||||||
<div>
|
<TableListItem
|
||||||
<div className="measurementLocation">
|
key={this.props.measurementData.measurementNumber}
|
||||||
{this.props.t(this.props.measurementData.label, {
|
itemKey={this.props.measurementData.measurementNumber}
|
||||||
keySeparator: '>',
|
itemClass={`measurementItem ${this.props.itemClass} ${hasWarningClass}`}
|
||||||
nsSeparator: '|',
|
itemIndex={this.props.itemIndex}
|
||||||
|
onItemClick={this.onItemClick}
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
<div className="measurementLocation">
|
||||||
|
{this.props.t(this.props.measurementData.label, {
|
||||||
|
keySeparator: '>',
|
||||||
|
nsSeparator: '|',
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="icons">
|
||||||
|
<div className="displayTexts">{this.getDataDisplayText()}</div>
|
||||||
|
<Icon
|
||||||
|
className={`eye-icon`}
|
||||||
|
name={this.state.visible ? 'eye' : 'eye-closed'}
|
||||||
|
width="20px"
|
||||||
|
height="20px"
|
||||||
|
onClick={() => {
|
||||||
|
this.props.measurementData.labels.forEach(label => {
|
||||||
|
label.visible = !this.state.visible;
|
||||||
|
});
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
visible: !this.state.visible,
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Icon
|
||||||
|
className={`angle-double-${
|
||||||
|
this.state.collapsed ? 'down' : 'up'
|
||||||
|
}`}
|
||||||
|
name={`angle-double-${this.state.collapsed ? 'down' : 'up'}`}
|
||||||
|
width="20px"
|
||||||
|
height="20px"
|
||||||
|
onClick={() => {
|
||||||
|
this.setState({
|
||||||
|
collapsed: !this.state.collapsed,
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</TableListItem>
|
||||||
|
{this.state.collapsed &&
|
||||||
|
this.props.measurementData.labels.map((SRLabel, index) => {
|
||||||
|
return (
|
||||||
|
<TableListItem
|
||||||
|
key={index}
|
||||||
|
itemKey={index}
|
||||||
|
itemMeta={<ColoredCircle color={SRLabel.color} />}
|
||||||
|
itemMetaClass="item-color-section"
|
||||||
|
onItemClick={this.onItemClick}
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
<div className="icons">
|
||||||
|
<span style={{ width: '90px' }}>
|
||||||
|
{SRLabel.label + ' : ' + SRLabel.value}
|
||||||
|
</span>
|
||||||
|
<Icon
|
||||||
|
className={`eye-icon`}
|
||||||
|
name={SRLabel.visible ? 'eye' : 'eye-closed'}
|
||||||
|
width="20px"
|
||||||
|
height="20px"
|
||||||
|
onClick={() => {
|
||||||
|
SRLabel.visible = !SRLabel.visible;
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</TableListItem>
|
||||||
|
);
|
||||||
})}
|
})}
|
||||||
|
</React.Fragment>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return (
|
||||||
|
<TableListItem
|
||||||
|
key={this.props.measurementData.measurementNumber}
|
||||||
|
itemKey={this.props.measurementData.measurementNumber}
|
||||||
|
itemClass={`measurementItem ${this.props.itemClass} ${hasWarningClass}`}
|
||||||
|
itemIndex={this.props.itemIndex}
|
||||||
|
onItemClick={this.onItemClick}
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
<div className="measurementLocation">
|
||||||
|
{this.props.t(this.props.measurementData.label, {
|
||||||
|
keySeparator: '>',
|
||||||
|
nsSeparator: '|',
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
<div className="displayTexts">{this.getDataDisplayText()}</div>
|
||||||
|
{!this.props.measurementData.isReadOnly && (
|
||||||
|
<div className="rowActions">{actionButtons}</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="displayTexts">{this.getDataDisplayText()}</div>
|
</TableListItem>
|
||||||
{!this.props.measurementData.isReadOnly && (
|
);
|
||||||
<div className="rowActions">{actionButtons}</div>
|
}
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</TableListItem>
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
onItemClick = event => {
|
onItemClick = event => {
|
||||||
|
|||||||
@@ -1,4 +1,16 @@
|
|||||||
.measurementItem
|
.measurementItem
|
||||||
|
.item-color-section
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
.item-color
|
||||||
|
margin-top: 8px;
|
||||||
|
border-radius: 100%;
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
|
||||||
.rowActions
|
.rowActions
|
||||||
margin-left: -1px;
|
margin-left: -1px;
|
||||||
background-color: var(--ui-gray-darker)
|
background-color: var(--ui-gray-darker)
|
||||||
@@ -63,3 +75,16 @@
|
|||||||
width: 22px;
|
width: 22px;
|
||||||
height: 20px;
|
height: 20px;
|
||||||
pointer-events: inherit;
|
pointer-events: inherit;
|
||||||
|
|
||||||
|
.icons
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
border-radius: 5px;
|
||||||
|
|
||||||
|
.icons .eye-icon
|
||||||
|
margin-left: 100px;
|
||||||
|
margin-right: -5px;
|
||||||
|
.icons .angle-double-down
|
||||||
|
.icons .angle-double-up
|
||||||
|
margin-left: 15px;
|
||||||
|
margin-right: -5px;
|
||||||
@@ -177,7 +177,9 @@ function Thumbnail(props) {
|
|||||||
const { progressId, progressData } = detail;
|
const { progressId, progressData } = detail;
|
||||||
if (`StackProgress:${displaySetInstanceUID}` === progressId) {
|
if (`StackProgress:${displaySetInstanceUID}` === progressId) {
|
||||||
const percent = progressData ? progressData.percentComplete : 0;
|
const percent = progressData ? progressData.percentComplete : 0;
|
||||||
setStackPercentComplete(percent);
|
if (percent > stackPercentComplete) {
|
||||||
|
setStackPercentComplete(percent);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}, 100);
|
}, 100);
|
||||||
|
|
||||||
|
|||||||
@@ -25,7 +25,8 @@
|
|||||||
box-shadow: inset 0 0 0 1px var(--ui-border-color-dark);
|
box-shadow: inset 0 0 0 1px var(--ui-border-color-dark);
|
||||||
border: 2px solid transparent;
|
border: 2px solid transparent;
|
||||||
border-radius: 12px;
|
border-radius: 12px;
|
||||||
height: 135px;
|
min-height: 135px;
|
||||||
|
max-height: 250px;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
padding: 5px;
|
padding: 5px;
|
||||||
position: relative;
|
position: relative;
|
||||||
@@ -36,6 +37,11 @@
|
|||||||
h1
|
h1
|
||||||
text-align: center
|
text-align: center
|
||||||
color: var(--text-primary-color);
|
color: var(--text-primary-color);
|
||||||
|
max-width: 200px;
|
||||||
|
max-height: 250px;
|
||||||
|
overflow-wrap: break-word;
|
||||||
|
word-wrap: break-word;
|
||||||
|
hyphens: auto;
|
||||||
|
|
||||||
.series-details
|
.series-details
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ $headerRowHeight = 63px
|
|||||||
|
|
||||||
.tableListHeaderTitle
|
.tableListHeaderTitle
|
||||||
color: var(--text-secondary-color)
|
color: var(--text-secondary-color)
|
||||||
|
max-width: 230px
|
||||||
font-size: 22px
|
font-size: 22px
|
||||||
font-weight: 300
|
font-weight: 300
|
||||||
line-height: $headerRowHeight
|
line-height: $headerRowHeight
|
||||||
|
|||||||
@@ -12,16 +12,13 @@ export class TableListItem extends Component {
|
|||||||
itemIndex: PropTypes.number,
|
itemIndex: PropTypes.number,
|
||||||
itemMeta: PropTypes.node,
|
itemMeta: PropTypes.node,
|
||||||
itemMetaClass: PropTypes.string,
|
itemMetaClass: PropTypes.string,
|
||||||
itemKey: PropTypes.oneOfType([
|
itemKey: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
|
||||||
PropTypes.string,
|
|
||||||
PropTypes.number,
|
|
||||||
]),
|
|
||||||
onItemClick: PropTypes.func.isRequired,
|
onItemClick: PropTypes.func.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
itemMeta: null,
|
itemMeta: null,
|
||||||
itemMetaClass: ''
|
itemMetaClass: '',
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
|||||||
@@ -3,6 +3,247 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
## [4.12.51](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.12.50...@ohif/viewer@4.12.51) (2023-03-19)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/viewer
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [4.12.50](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.12.49...@ohif/viewer@4.12.50) (2023-01-17)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/viewer
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [4.12.49](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.12.48...@ohif/viewer@4.12.49) (2023-01-10)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/viewer
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [4.12.48](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.12.47...@ohif/viewer@4.12.48) (2022-12-21)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/viewer
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [4.12.47](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.12.46...@ohif/viewer@4.12.47) (2022-12-21)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/viewer
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [4.12.46](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.12.45...@ohif/viewer@4.12.46) (2022-12-12)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* [#2964](https://github.com/OHIF/Viewers/issues/2964) Reword message for segmentation error loading due to orientation tolerance ([#3017](https://github.com/OHIF/Viewers/issues/3017)) ([597ac11](https://github.com/OHIF/Viewers/commit/597ac11c4daee1b1e14148804551e20611cfef08))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [4.12.45](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.12.44...@ohif/viewer@4.12.45) (2022-11-12)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/viewer
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [4.12.44](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.12.43...@ohif/viewer@4.12.44) (2022-11-02)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/viewer
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [4.12.43](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.12.42...@ohif/viewer@4.12.43) (2022-10-28)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* [#2964](https://github.com/OHIF/Viewers/issues/2964) Update message for segmentation error loading due to orientation tolerance ([#2982](https://github.com/OHIF/Viewers/issues/2982)) ([545161d](https://github.com/OHIF/Viewers/commit/545161d5e8d92bb6652f17dc24a36dac8aa7b3a4))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [4.12.42](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.12.41...@ohif/viewer@4.12.42) (2022-10-25)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/viewer
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [4.12.41](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.12.40...@ohif/viewer@4.12.41) (2022-09-26)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/viewer
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [4.12.40](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.12.39...@ohif/viewer@4.12.40) (2022-09-20)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/viewer
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [4.12.39](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.12.38...@ohif/viewer@4.12.39) (2022-09-19)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/viewer
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [4.12.38](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.12.37...@ohif/viewer@4.12.38) (2022-09-12)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/viewer
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [4.12.37](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.12.36...@ohif/viewer@4.12.37) (2022-09-12)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/viewer
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [4.12.36](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.12.35...@ohif/viewer@4.12.36) (2022-09-08)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* hotkey page up/down did not work ([#2910](https://github.com/OHIF/Viewers/issues/2910)) ([d74ad37](https://github.com/OHIF/Viewers/commit/d74ad370e091c8af2d93ec35f688c4ef902bab2b))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [4.12.35](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.12.34...@ohif/viewer@4.12.35) (2022-09-02)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/viewer
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [4.12.34](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.12.33...@ohif/viewer@4.12.34) (2022-09-01)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/viewer
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [4.12.33](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.12.32...@ohif/viewer@4.12.33) (2022-08-31)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/viewer
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [4.12.32](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.12.31...@ohif/viewer@4.12.32) (2022-08-25)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/viewer
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [4.12.31](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.12.30...@ohif/viewer@4.12.31) (2022-08-24)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/viewer
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [4.12.30](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.12.29...@ohif/viewer@4.12.30) (2022-06-30)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/viewer
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [4.12.29](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.12.28...@ohif/viewer@4.12.29) (2022-06-29)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/viewer
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [4.12.28](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.12.27...@ohif/viewer@4.12.28) (2022-06-28)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/viewer
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [4.12.27](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.12.26...@ohif/viewer@4.12.27) (2022-06-14)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/viewer
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [4.12.26](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.12.25...@ohif/viewer@4.12.26) (2022-04-22)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/viewer
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [4.12.25](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.12.24...@ohif/viewer@4.12.25) (2022-04-06)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/viewer
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [4.12.24](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.12.23...@ohif/viewer@4.12.24) (2022-04-05)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/viewer
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [4.12.23](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.12.22...@ohif/viewer@4.12.23) (2022-03-25)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @ohif/viewer
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## [4.12.22](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.12.21...@ohif/viewer@4.12.22) (2022-03-14)
|
## [4.12.22](https://github.com/OHIF/Viewers/compare/@ohif/viewer@4.12.21...@ohif/viewer@4.12.22) (2022-03-14)
|
||||||
|
|
||||||
**Note:** Version bump only for package @ohif/viewer
|
**Note:** Version bump only for package @ohif/viewer
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@ohif/viewer",
|
"name": "@ohif/viewer",
|
||||||
"version": "4.12.22",
|
"version": "4.12.51",
|
||||||
"description": "OHIF Viewer",
|
"description": "OHIF Viewer",
|
||||||
"author": "OHIF Contributors",
|
"author": "OHIF Contributors",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
@@ -47,26 +47,26 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "^7.5.5",
|
"@babel/runtime": "^7.5.5",
|
||||||
"@ohif/core": "^2.16.14",
|
"@ohif/core": "^2.16.35",
|
||||||
"@ohif/extension-cornerstone": "^2.12.8",
|
"@ohif/extension-cornerstone": "^2.12.16",
|
||||||
"@ohif/extension-debugging": "^0.3.2",
|
"@ohif/extension-debugging": "^0.3.3",
|
||||||
"@ohif/extension-dicom-html": "^1.3.19",
|
"@ohif/extension-dicom-html": "^1.3.24",
|
||||||
"@ohif/extension-dicom-microscopy": "^0.52.0",
|
"@ohif/extension-dicom-microscopy": "^0.52.0",
|
||||||
"@ohif/extension-dicom-pdf": "^1.1.2",
|
"@ohif/extension-dicom-pdf": "^1.1.2",
|
||||||
"@ohif/extension-dicom-rt": "^0.7.9",
|
"@ohif/extension-dicom-rt": "^0.7.14",
|
||||||
"@ohif/extension-dicom-segmentation": "^0.7.9",
|
"@ohif/extension-dicom-segmentation": "^0.7.21",
|
||||||
"@ohif/extension-lesion-tracker": "^0.2.1",
|
"@ohif/extension-lesion-tracker": "^0.2.2",
|
||||||
"@ohif/extension-vtk": "^1.12.19",
|
"@ohif/extension-vtk": "^1.12.42",
|
||||||
"@ohif/i18n": "^0.53.0",
|
"@ohif/i18n": "^0.53.0",
|
||||||
"@ohif/ui": "^1.10.7",
|
"@ohif/ui": "^1.10.11",
|
||||||
"@tanem/react-nprogress": "^1.1.25",
|
"@tanem/react-nprogress": "^1.1.25",
|
||||||
"classnames": "^2.2.6",
|
"classnames": "^2.2.6",
|
||||||
"core-js": "^3.2.1",
|
"core-js": "^3.2.1",
|
||||||
"cornerstone-core": "^2.6.1",
|
"cornerstone-core": "^2.6.1",
|
||||||
"cornerstone-math": "^0.1.9",
|
"cornerstone-math": "^0.1.9",
|
||||||
"cornerstone-tools": "^6.0.6",
|
"cornerstone-tools": "^6.0.6",
|
||||||
"cornerstone-wado-image-loader": "^4.1.0",
|
"cornerstone-wado-image-loader": "^4.2.1",
|
||||||
"dcmjs": "0.19.7",
|
"dcmjs": "0.29.3",
|
||||||
"dicom-parser": "^1.8.11",
|
"dicom-parser": "^1.8.11",
|
||||||
"dicomweb-client": "^0.8.3",
|
"dicomweb-client": "^0.8.3",
|
||||||
"hammerjs": "^2.0.8",
|
"hammerjs": "^2.0.8",
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ window.config = {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|
||||||
// Extensions should be able to suggest default values for these?
|
// Extensions should be able to suggest default values for these?
|
||||||
// Or we can require that these be explicitly set
|
// Or we can require that these be explicitly set
|
||||||
hotkeys: [
|
hotkeys: [
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { redux } from '@ohif/core';
|
import { redux, utils } from '@ohif/core';
|
||||||
import store from './../../store';
|
import store from './../../store';
|
||||||
|
|
||||||
const commandsModule = ({ commandsManager }) => {
|
const commandsModule = ({ commandsManager }) => {
|
||||||
|
|||||||
@@ -122,6 +122,7 @@ function convertMeasurementsToTableData(toolCollections, timepoints) {
|
|||||||
|
|
||||||
Object.keys(toolCollections).forEach(toolId => {
|
Object.keys(toolCollections).forEach(toolId => {
|
||||||
const toolMeasurements = toolCollections[toolId];
|
const toolMeasurements = toolCollections[toolId];
|
||||||
|
|
||||||
const tool = tools.find(tool => tool.id === toolId);
|
const tool = tools.find(tool => tool.id === toolId);
|
||||||
const { displayFunction } = tool.options.measurementTable;
|
const { displayFunction } = tool.options.measurementTable;
|
||||||
|
|
||||||
@@ -131,12 +132,13 @@ function convertMeasurementsToTableData(toolCollections, timepoints) {
|
|||||||
Object.keys(groupedMeasurements).forEach(groupedMeasurementsIndex => {
|
Object.keys(groupedMeasurements).forEach(groupedMeasurementsIndex => {
|
||||||
const measurementNumberList =
|
const measurementNumberList =
|
||||||
groupedMeasurements[groupedMeasurementsIndex];
|
groupedMeasurements[groupedMeasurementsIndex];
|
||||||
|
|
||||||
const measurementData = measurementNumberList[0];
|
const measurementData = measurementNumberList[0];
|
||||||
const {
|
const {
|
||||||
measurementNumber,
|
measurementNumber,
|
||||||
lesionNamingNumber,
|
lesionNamingNumber,
|
||||||
toolType,
|
toolType,
|
||||||
isReadOnly
|
isReadOnly,
|
||||||
} = measurementData;
|
} = measurementData;
|
||||||
const measurementId = measurementData._id;
|
const measurementId = measurementData._id;
|
||||||
|
|
||||||
@@ -150,6 +152,8 @@ function convertMeasurementsToTableData(toolCollections, timepoints) {
|
|||||||
const tableMeasurement = {
|
const tableMeasurement = {
|
||||||
itemNumber: lesionNamingNumber,
|
itemNumber: lesionNamingNumber,
|
||||||
label: getMeasurementText(measurementData),
|
label: getMeasurementText(measurementData),
|
||||||
|
labels: measurementData.labels,
|
||||||
|
isSRText: measurementData.isSRText,
|
||||||
measurementId,
|
measurementId,
|
||||||
measurementNumber,
|
measurementNumber,
|
||||||
lesionNamingNumber,
|
lesionNamingNumber,
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { measurements, utils } from '@ohif/core';
|
import { measurements, utils } from '@ohif/core';
|
||||||
|
import { servicesManager } from './../../App.js';
|
||||||
const { MeasurementApi } = measurements;
|
const { MeasurementApi } = measurements;
|
||||||
const { studyMetadataManager } = utils;
|
const { studyMetadataManager } = utils;
|
||||||
|
|
||||||
@@ -19,6 +19,25 @@ export default function jumpToRowItem(
|
|||||||
timepointManagerState,
|
timepointManagerState,
|
||||||
options = { invertViewportTimepointsOrder: false, childToolKey: null }
|
options = { invertViewportTimepointsOrder: false, childToolKey: null }
|
||||||
) {
|
) {
|
||||||
|
const viewports = viewportsState.layout.viewports;
|
||||||
|
const activeViewportIndex = viewportsState.activeViewportIndex;
|
||||||
|
const activeViewport = viewports[activeViewportIndex];
|
||||||
|
if (activeViewport.vtk) {
|
||||||
|
const error = new Error('Measurements are not supported by the MPR mode.');
|
||||||
|
const { UINotificationService, LoggerService } = servicesManager.services;
|
||||||
|
LoggerService.error({ error, message: error.message });
|
||||||
|
UINotificationService.show({
|
||||||
|
title: 'Measurements panel',
|
||||||
|
message: error.message,
|
||||||
|
type: 'warning',
|
||||||
|
autoClose: true,
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
viewportSpecificData: [],
|
||||||
|
layout: [], // TODO: if we need to change layout, we should return this here
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
const numViewports = viewportsState.layout.viewports.length;
|
const numViewports = viewportsState.layout.viewports.length;
|
||||||
const numTimepoints = timepointManagerState.timepoints.length;
|
const numTimepoints = timepointManagerState.timepoints.length;
|
||||||
const { measurements, timepoints } = timepointManagerState;
|
const { measurements, timepoints } = timepointManagerState;
|
||||||
|
|||||||
@@ -20,12 +20,20 @@ const mapDispatchToProps = (dispatch, ownProps) => {
|
|||||||
const { Modality } = displaySet;
|
const { Modality } = displaySet;
|
||||||
if (Modality === 'SEG' && servicesManager) {
|
if (Modality === 'SEG' && servicesManager) {
|
||||||
const onDisplaySetLoadFailureHandler = error => {
|
const onDisplaySetLoadFailureHandler = error => {
|
||||||
LoggerService.error({ error, message: error.message });
|
const message =
|
||||||
|
error.message.includes('orthogonal') ||
|
||||||
|
error.message.includes('oblique')
|
||||||
|
? 'The segmentation has been detected as non coplanar,\
|
||||||
|
If you really think it is coplanar,\
|
||||||
|
please adjust the tolerance in the segmentation panel settings (at your own peril!)'
|
||||||
|
: error.message;
|
||||||
|
|
||||||
|
LoggerService.error({ error, message });
|
||||||
UINotificationService.show({
|
UINotificationService.show({
|
||||||
title: 'DICOM Segmentation Loader',
|
title: 'DICOM Segmentation Loader',
|
||||||
message: error.message,
|
message,
|
||||||
type: 'error',
|
type: 'error',
|
||||||
autoClose: true,
|
autoClose: false,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -46,9 +54,11 @@ const mapDispatchToProps = (dispatch, ownProps) => {
|
|||||||
detail: { activatedLabelmapIndex: activatedLabelmapIndex },
|
detail: { activatedLabelmapIndex: activatedLabelmapIndex },
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
const segThumbnailSelected = new CustomEvent('segseriesselected');
|
||||||
document.dispatchEvent(selectionFired);
|
document.dispatchEvent(selectionFired);
|
||||||
|
document.dispatchEvent(segThumbnailSelected);
|
||||||
});
|
});
|
||||||
} else {
|
} else if (Modality !== 'SR') {
|
||||||
displaySet = displaySet.getSourceDisplaySet(ownProps.studyMetadata);
|
displaySet = displaySet.getSourceDisplaySet(ownProps.studyMetadata);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -79,7 +89,7 @@ const mapDispatchToProps = (dispatch, ownProps) => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (displaySet.isModalitySupported === false) {
|
if (displaySet.isSOPClassUIDSupported === false) {
|
||||||
const error = new Error('Modality not supported');
|
const error = new Error('Modality not supported');
|
||||||
const message = 'Modality not supported';
|
const message = 'Modality not supported';
|
||||||
LoggerService.error({ error, message });
|
LoggerService.error({ error, message });
|
||||||
|
|||||||
@@ -105,6 +105,11 @@ class Viewer extends Component {
|
|||||||
if (this.props.dialog) {
|
if (this.props.dialog) {
|
||||||
this.props.dialog.dismissAll();
|
this.props.dialog.dismissAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
document.removeEventListener(
|
||||||
|
'segmentationLoadingError',
|
||||||
|
this._updateThumbnails
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
retrieveTimepoints = filter => {
|
retrieveTimepoints = filter => {
|
||||||
@@ -212,6 +217,12 @@ class Viewer extends Component {
|
|||||||
),
|
),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
document.addEventListener(
|
||||||
|
'segmentationLoadingError',
|
||||||
|
this._updateThumbnails.bind(this),
|
||||||
|
false
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidUpdate(prevProps) {
|
componentDidUpdate(prevProps) {
|
||||||
@@ -251,10 +262,28 @@ class Viewer extends Component {
|
|||||||
const { currentTimepointId } = this;
|
const { currentTimepointId } = this;
|
||||||
|
|
||||||
this.timepointApi.retrieveTimepoints({ PatientID });
|
this.timepointApi.retrieveTimepoints({ PatientID });
|
||||||
this.measurementApi.retrieveMeasurements(PatientID, [currentTimepointId]);
|
this.measurementApi
|
||||||
|
.retrieveMeasurements(PatientID, [currentTimepointId])
|
||||||
|
.then(() => {
|
||||||
|
this._updateThumbnails();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_updateThumbnails() {
|
||||||
|
const { studies, activeViewportIndex, viewports } = this.props;
|
||||||
|
|
||||||
|
const activeViewport = viewports[activeViewportIndex];
|
||||||
|
const activeDisplaySetInstanceUID = activeViewport
|
||||||
|
? activeViewport.displaySetInstanceUID
|
||||||
|
: undefined;
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
thumbnails: _mapStudiesToThumbnails(studies, activeDisplaySetInstanceUID),
|
||||||
|
activeDisplaySetInstanceUID,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
_getActiveViewport() {
|
_getActiveViewport() {
|
||||||
return this.props.viewports[this.props.activeViewportIndex];
|
return this.props.viewports[this.props.activeViewportIndex];
|
||||||
}
|
}
|
||||||
@@ -444,7 +473,7 @@ const _checkForDerivedDisplaySets = async function(displaySet, study) {
|
|||||||
let derivedDisplaySetsNumber = 0;
|
let derivedDisplaySetsNumber = 0;
|
||||||
if (
|
if (
|
||||||
displaySet.Modality &&
|
displaySet.Modality &&
|
||||||
!['SEG', 'SR', 'RTSTRUCT', 'RTDOSE'].includes(displaySet.Modality)
|
!['SEG', 'SR', 'RTSTRUCT'].includes(displaySet.Modality)
|
||||||
) {
|
) {
|
||||||
const studyMetadata = studyMetadataManager.get(study.StudyInstanceUID);
|
const studyMetadata = studyMetadataManager.get(study.StudyInstanceUID);
|
||||||
|
|
||||||
@@ -461,9 +490,7 @@ const _checkForDerivedDisplaySets = async function(displaySet, study) {
|
|||||||
/**
|
/**
|
||||||
* Async function to check if there are any inconsistences in the series.
|
* Async function to check if there are any inconsistences in the series.
|
||||||
*
|
*
|
||||||
* For segmentation checks that the geometry is consistent with the source images:
|
* For segmentation returns any error during loading.
|
||||||
* 1) no frames out of plane;
|
|
||||||
* 2) have the same width and height.
|
|
||||||
*
|
*
|
||||||
* For reconstructable 3D volume:
|
* For reconstructable 3D volume:
|
||||||
* 1) Is series multiframe?
|
* 1) Is series multiframe?
|
||||||
@@ -477,18 +504,15 @@ const _checkForDerivedDisplaySets = async function(displaySet, study) {
|
|||||||
* @param {*object} displaySet
|
* @param {*object} displaySet
|
||||||
* @returns {[string]} an array of strings containing the warnings
|
* @returns {[string]} an array of strings containing the warnings
|
||||||
*/
|
*/
|
||||||
const _checkForSeriesInconsistencesWarnings = async function(
|
const _checkForSeriesInconsistencesWarnings = async function(displaySet) {
|
||||||
displaySet,
|
|
||||||
studies
|
|
||||||
) {
|
|
||||||
if (displaySet.inconsistencyWarnings) {
|
|
||||||
// warnings already checked and cached in displaySet
|
|
||||||
return displaySet.inconsistencyWarnings;
|
|
||||||
}
|
|
||||||
|
|
||||||
const inconsistencyWarnings = [];
|
const inconsistencyWarnings = [];
|
||||||
|
|
||||||
if (displaySet.Modality !== 'SEG') {
|
if (displaySet.Modality !== 'SEG') {
|
||||||
|
// warnings already checked and cached in displaySet
|
||||||
|
if (displaySet.inconsistencyWarnings) {
|
||||||
|
return displaySet.inconsistencyWarnings;
|
||||||
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
displaySet.reconstructionIssues &&
|
displaySet.reconstructionIssues &&
|
||||||
displaySet.reconstructionIssues.length !== 0
|
displaySet.reconstructionIssues.length !== 0
|
||||||
@@ -543,101 +567,17 @@ const _checkForSeriesInconsistencesWarnings = async function(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (displaySet.isModalitySupported === false) {
|
if (displaySet.isSOPClassUIDSupported === false) {
|
||||||
inconsistencyWarnings.push('The datasets modality is not supported.');
|
inconsistencyWarnings.push('The datasets is not supported.');
|
||||||
}
|
}
|
||||||
|
displaySet.inconsistencyWarnings = inconsistencyWarnings;
|
||||||
} else {
|
} else {
|
||||||
const segMetadata = displaySet.metadata;
|
if (displaySet.loadError) {
|
||||||
if (!segMetadata) {
|
inconsistencyWarnings.push(displaySet.segLoadErrorMessagge);
|
||||||
return inconsistencyWarnings;
|
displaySet.inconsistencyWarnings = inconsistencyWarnings;
|
||||||
}
|
|
||||||
|
|
||||||
const { referencedDisplaySet } = displaySet.getSourceDisplaySet(
|
|
||||||
studies,
|
|
||||||
false
|
|
||||||
);
|
|
||||||
if (!referencedDisplaySet) {
|
|
||||||
return inconsistencyWarnings;
|
|
||||||
}
|
|
||||||
|
|
||||||
const imageIds = referencedDisplaySet.images.map(image =>
|
|
||||||
image.getImageId()
|
|
||||||
);
|
|
||||||
if (!imageIds || imageIds.length === 0) {
|
|
||||||
return inconsistencyWarnings;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (
|
|
||||||
let i = 0,
|
|
||||||
groupsLen = segMetadata.PerFrameFunctionalGroupsSequence.length;
|
|
||||||
i < groupsLen;
|
|
||||||
++i
|
|
||||||
) {
|
|
||||||
const PerFrameFunctionalGroups =
|
|
||||||
segMetadata.PerFrameFunctionalGroupsSequence[i];
|
|
||||||
if (!PerFrameFunctionalGroups) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
let SourceImageSequence = undefined;
|
|
||||||
if (segMetadata.SourceImageSequence) {
|
|
||||||
SourceImageSequence = segMetadata.SourceImageSequence[i];
|
|
||||||
} else if (PerFrameFunctionalGroups.DerivationImageSequence) {
|
|
||||||
SourceImageSequence =
|
|
||||||
PerFrameFunctionalGroups.DerivationImageSequence.SourceImageSequence;
|
|
||||||
}
|
|
||||||
if (!SourceImageSequence) {
|
|
||||||
if (inconsistencyWarnings.length === 0) {
|
|
||||||
const warningMessage =
|
|
||||||
'The segmentation ' +
|
|
||||||
'has frames out of plane respect to the source images.';
|
|
||||||
inconsistencyWarnings.push(warningMessage);
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const { ReferencedSOPInstanceUID } = SourceImageSequence;
|
|
||||||
|
|
||||||
const imageId = imageIds.find(imageId => {
|
|
||||||
const sopCommonModule = cornerstone.metaData.get(
|
|
||||||
'sopCommonModule',
|
|
||||||
imageId
|
|
||||||
);
|
|
||||||
if (!sopCommonModule) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
return sopCommonModule.sopInstanceUID === ReferencedSOPInstanceUID;
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!imageId) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const sourceImageMetadata = cornerstone.metaData.get('instance', imageId);
|
|
||||||
if (
|
|
||||||
segMetadata.Rows !== sourceImageMetadata.Rows ||
|
|
||||||
segMetadata.Columns !== sourceImageMetadata.Columns
|
|
||||||
) {
|
|
||||||
const warningMessage =
|
|
||||||
'The segmentation ' +
|
|
||||||
'has frames with different geometry ' +
|
|
||||||
'dimensions (Rows and Columns) respect to the source images.';
|
|
||||||
inconsistencyWarnings.push(warningMessage);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (inconsistencyWarnings.length !== 0) {
|
|
||||||
const warningMessage =
|
|
||||||
'The segmentation format is not supported yet. ' +
|
|
||||||
'The segmentation data (segments) could not be loaded.';
|
|
||||||
inconsistencyWarnings.push(warningMessage);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// cache the warnings
|
|
||||||
displaySet.inconsistencyWarnings = inconsistencyWarnings;
|
|
||||||
return inconsistencyWarnings;
|
return inconsistencyWarnings;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -672,9 +612,29 @@ const _isDisplaySetActive = function(
|
|||||||
if (
|
if (
|
||||||
displaySet.Modality !== 'SEG' &&
|
displaySet.Modality !== 'SEG' &&
|
||||||
displaySet.Modality !== 'RTSTRUCT' &&
|
displaySet.Modality !== 'RTSTRUCT' &&
|
||||||
displaySet.Modality !== 'RTDOSE'
|
displaySet.Modality !== 'SR'
|
||||||
) {
|
) {
|
||||||
active = activeDisplaySetInstanceUID === displaySetInstanceUID;
|
active = activeDisplaySetInstanceUID === displaySetInstanceUID;
|
||||||
|
} else if (displaySet.Modality === 'SR') {
|
||||||
|
active = activeDisplaySetInstanceUID === displaySetInstanceUID;
|
||||||
|
|
||||||
|
if (!active && displaySet.getSourceDisplaySet) {
|
||||||
|
const referencedDisplaySet = displaySet.getSourceDisplaySet(
|
||||||
|
studies,
|
||||||
|
false
|
||||||
|
);
|
||||||
|
if (referencedDisplaySet && referencedDisplaySet.length !== 0) {
|
||||||
|
for (let i = 0; i < referencedDisplaySet.length; i++) {
|
||||||
|
if (
|
||||||
|
referencedDisplaySet[i].displaySetInstanceUID ===
|
||||||
|
activeDisplaySetInstanceUID
|
||||||
|
) {
|
||||||
|
active = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
} else if (displaySet.getSourceDisplaySet) {
|
} else if (displaySet.getSourceDisplaySet) {
|
||||||
if (displaySet.Modality === 'SEG') {
|
if (displaySet.Modality === 'SEG') {
|
||||||
const { referencedDisplaySet } = displaySet.getSourceDisplaySet(
|
const { referencedDisplaySet } = displaySet.getSourceDisplaySet(
|
||||||
@@ -725,21 +685,19 @@ const _mapStudiesToThumbnails = function(studies, activeDisplaySetInstanceUID) {
|
|||||||
let altImageText;
|
let altImageText;
|
||||||
|
|
||||||
if (displaySet.Modality && displaySet.Modality === 'SEG') {
|
if (displaySet.Modality && displaySet.Modality === 'SEG') {
|
||||||
// TODO: We want to replace this with a thumbnail showing
|
|
||||||
// the segmentation map on the image, but this is easier
|
|
||||||
// and better than what we have right now.
|
|
||||||
altImageText = 'SEG';
|
altImageText = 'SEG';
|
||||||
|
} else if (displaySet.Modality && displaySet.Modality === 'SR') {
|
||||||
|
altImageText = 'SR';
|
||||||
} else if (displaySet.images && displaySet.images.length) {
|
} else if (displaySet.images && displaySet.images.length) {
|
||||||
const imageIndex = Math.floor(displaySet.images.length / 2);
|
const imageIndex = Math.floor(displaySet.images.length / 2);
|
||||||
imageId = displaySet.images[imageIndex].getImageId();
|
imageId = displaySet.images[imageIndex].getImageId();
|
||||||
|
} else if (displaySet.isSOPClassUIDSupported === false) {
|
||||||
|
altImageText = displaySet.SOPClassUIDNaturalized;
|
||||||
} else {
|
} else {
|
||||||
altImageText = displaySet.Modality ? displaySet.Modality : 'UN';
|
altImageText = displaySet.Modality ? displaySet.Modality : 'UN';
|
||||||
}
|
}
|
||||||
|
|
||||||
const hasWarnings = _checkForSeriesInconsistencesWarnings(
|
const hasWarnings = _checkForSeriesInconsistencesWarnings(displaySet);
|
||||||
displaySet,
|
|
||||||
studies
|
|
||||||
);
|
|
||||||
|
|
||||||
const hasDerivedDisplaySets = _checkForDerivedDisplaySets(
|
const hasDerivedDisplaySets = _checkForDerivedDisplaySets(
|
||||||
displaySet,
|
displaySet,
|
||||||
|
|||||||
@@ -145,22 +145,42 @@ class ViewerMain extends Component {
|
|||||||
const { Modality } = displaySet;
|
const { Modality } = displaySet;
|
||||||
if (Modality === 'SEG' && servicesManager) {
|
if (Modality === 'SEG' && servicesManager) {
|
||||||
const onDisplaySetLoadFailureHandler = error => {
|
const onDisplaySetLoadFailureHandler = error => {
|
||||||
LoggerService.error({ error, message: error.message });
|
const message =
|
||||||
|
error.message.includes('orthogonal') ||
|
||||||
|
error.message.includes('oblique')
|
||||||
|
? 'The segmentation has been detected as non coplanar,\
|
||||||
|
If you really think it is coplanar,\
|
||||||
|
please adjust the tolerance in the segmentation panel settings (at your own peril!)'
|
||||||
|
: error.message;
|
||||||
|
LoggerService.error({ error, message });
|
||||||
UINotificationService.show({
|
UINotificationService.show({
|
||||||
title: 'DICOM Segmentation Loader',
|
title: 'DICOM Segmentation Loader',
|
||||||
message: error.message,
|
message,
|
||||||
type: 'error',
|
type: 'error',
|
||||||
autoClose: true,
|
autoClose: false,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const { referencedDisplaySet } = displaySet.getSourceDisplaySet(
|
const {
|
||||||
|
referencedDisplaySet,
|
||||||
|
activatedLabelmapPromise,
|
||||||
|
} = displaySet.getSourceDisplaySet(
|
||||||
this.props.studies,
|
this.props.studies,
|
||||||
true,
|
true,
|
||||||
onDisplaySetLoadFailureHandler
|
onDisplaySetLoadFailureHandler
|
||||||
);
|
);
|
||||||
displaySet = referencedDisplaySet;
|
displaySet = referencedDisplaySet;
|
||||||
} else {
|
|
||||||
|
activatedLabelmapPromise.then(activatedLabelmapIndex => {
|
||||||
|
const selectionFired = new CustomEvent(
|
||||||
|
'extensiondicomsegmentationsegselected',
|
||||||
|
{
|
||||||
|
detail: { activatedLabelmapIndex: activatedLabelmapIndex },
|
||||||
|
}
|
||||||
|
);
|
||||||
|
document.dispatchEvent(selectionFired);
|
||||||
|
});
|
||||||
|
} else if (Modality !== 'SR') {
|
||||||
displaySet = displaySet.getSourceDisplaySet(this.props.studies);
|
displaySet = displaySet.getSourceDisplaySet(this.props.studies);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -177,7 +197,7 @@ class ViewerMain extends Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (displaySet.isModalitySupported === false) {
|
if (displaySet.isSOPClassUIDSupported === false) {
|
||||||
const error = new Error('Modality not supported');
|
const error = new Error('Modality not supported');
|
||||||
const message = 'Modality not supported';
|
const message = 'Modality not supported';
|
||||||
LoggerService.error({ error, message });
|
LoggerService.error({ error, message });
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ function DicomStorePickerModal({
|
|||||||
user,
|
user,
|
||||||
url,
|
url,
|
||||||
},
|
},
|
||||||
onClose
|
onClose,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
+1
-1
@@ -1 +1 @@
|
|||||||
3.7
|
3.8
|
||||||
Reference in new issue
Block a user