Compare commits

..
Author SHA1 Message Date
ohif-bot eaa423c64e chore(release): publish [skip ci]
- @ohif/extension-vtk@1.12.36
 - @ohif/core@2.16.30
 - @ohif/viewer@4.12.42
2022-10-25 14:09:04 +00:00
Gitanjali f7db74a843 fix: 2965 Correct Parsing Logic for Qualitative Instance Level SR (#2972)
* fix: 2965 Correct Parsing Logic for Qualitative Instance Level SR

* fix: 2965 Correct Parsing Logic for Qualitative Instance Level SR

* fix: 2965 Correct Parsing Logic for Qualitative Instance Level SR
2022-10-25 10:06:59 -04:00
ohif-bot 7aba01f534 chore(release): publish [skip ci]
- @ohif/extension-vtk@1.12.35
 - @ohif/viewer@4.12.41
2022-09-26 14:42:01 +00:00
GitanjaliChhetri c5ebeaa127 Re IDC #2847: Fix 2d MPR rendering issue for the sagittal view (#2960) 2022-09-26 08:39:55 -06:00
ohif-bot d7e1d2c975 chore(release): publish [skip ci]
- @ohif/extension-cornerstone@2.12.15
 - @ohif/extension-vtk@1.12.34
 - @ohif/core@2.16.29
 - @ohif/viewer@4.12.40
2022-09-20 11:26:05 +00:00
Davide Punzo eb7eb56d7e Re IDC #2934: update cornerstoneWADOImageLoader to v 4.2.1 (#2944) 2022-09-20 13:24:17 +02:00
ohif-bot c5ee092bf2 chore(release): publish [skip ci]
- @ohif/extension-dicom-segmentation@0.7.15
 - @ohif/extension-vtk@1.12.33
 - @ohif/core@2.16.28
 - @ohif/viewer@4.12.39
2022-09-19 10:32:07 +00:00
Davide Punzo e2f4f95222 Re IDC #2935: fix parsing of Instance-level qualitative annotations (#2940) 2022-09-19 12:29:28 +02:00
ohif-bot 4546ede9bb chore(release): publish [skip ci]
- @ohif/extension-dicom-segmentation@0.7.14
 - @ohif/viewer@4.12.38
2022-09-12 12:24:25 +00:00
Davide Punzo e0fb439e98 Fix IDC Re #2918: Runtime tolerance for SEGs loading (#2924) 2022-09-12 14:22:32 +02:00
ohif-bot ebbc9e0d6d chore(release): publish [skip ci]
- @ohif/extension-vtk@1.12.32
 - @ohif/core@2.16.27
 - @ohif/viewer@4.12.37
2022-09-12 08:27:33 +00:00
Davide Punzo 47a6130fd5 Re IDC #2919: fix popup notifications behavior (#2923) 2022-09-12 10:25:19 +02:00
ohif-bot 80dfdcc37c chore(release): publish [skip ci]
- @ohif/viewer@4.12.36
2022-09-08 14:48:41 +00:00
Doug Horner d74ad370e0 fix: hotkey page up/down did not work (#2910) 2022-09-08 10:46:26 -04:00
ohif-bot ddfde9279e chore(release): publish [skip ci]
- @ohif/extension-vtk@1.12.31
 - @ohif/core@2.16.26
 - @ohif/viewer@4.12.35
2022-09-02 13:49:07 +00:00
Davide Punzo aaab6d2622 Re IDC #2849: raise error if dicom request is not conforming with the UID (DICOM UI VR) character repertoire. (#2908) 2022-09-02 15:47:28 +02:00
ohif-bot d2921083e7 chore(release): publish [skip ci]
- @ohif/extension-cornerstone@2.12.14
 - @ohif/extension-dicom-html@1.3.23
 - @ohif/extension-dicom-rt@0.7.13
 - @ohif/extension-dicom-segmentation@0.7.13
 - @ohif/extension-dicom-tag-browser@0.2.24
 - @ohif/extension-vtk@1.12.30
 - @ohif/core@2.16.25
 - @ohif/viewer@4.12.34
2022-09-01 18:14:46 +00:00
Davide Punzo a6338ea266 Re IDC #2869: update dcmjs for fixing eps in nearly equal condition (#2906) 2022-09-01 20:12:30 +02:00
ohif-bot fe9551b9b1 chore(release): publish [skip ci]
- @ohif/extension-dicom-rt@0.7.12
 - @ohif/extension-dicom-segmentation@0.7.12
 - @ohif/extension-vtk@1.12.29
 - @ohif/core@2.16.24
 - @ohif/viewer@4.12.33
2022-08-31 12:59:14 +00:00
Davide Punzo ef60156a47 Fix minor issues IDC (#2903) 2022-08-31 14:57:14 +02:00
ohif-bot 850de2903a chore(release): publish [skip ci]
- @ohif/viewer@4.12.32
2022-08-25 09:50:05 +00:00
Davide Punzo f26ed5539b IDC2859 (#2891)
* Re #2859 IDC: disable jump to for measurements in MPR mode
2022-08-25 11:48:20 +02:00
ohif-bot 21aeaa63c7 chore(release): publish [skip ci]
- @ohif/extension-vtk@1.12.28
 - @ohif/core@2.16.23
 - @ohif/ui@1.10.10
 - @ohif/viewer@4.12.31
2022-08-24 10:38:46 +00:00
Davide Punzo 2ac0d3f017 Re #2852 IDC: fix parsing of qualitative slice annotation (#2888) 2022-08-24 12:37:21 +02:00
ohif-bot efdbe8e7b4 chore(release): publish [skip ci]
- @ohif/extension-cornerstone@2.12.13
 - @ohif/extension-debugging@0.3.3
 - @ohif/extension-dicom-html@1.3.22
 - @ohif/extension-vtk@1.12.27
 - @ohif/core@2.16.22
 - @ohif/viewer@4.12.30
2022-06-30 10:07:05 +00:00
Davide Punzo 67b70eaa98 IDC2797: add linking and report UI for SR displaySets (#2834) 2022-06-30 12:05:47 +02:00
ohif-bot 5150ed9427 chore(release): publish [skip ci]
- @ohif/extension-cornerstone@2.12.12
 - @ohif/extension-lesion-tracker@0.2.2
 - @ohif/extension-vtk@1.12.26
 - @ohif/core@2.16.21
 - @ohif/ui@1.10.9
 - @ohif/viewer@4.12.29
2022-06-29 02:26:24 +00:00
Davide Punzo 1bcb0acf9f IDC2797: improving UI for SR qualitative measurements and bug fixes (#2832) 2022-06-29 04:24:35 +02:00
ohif-bot 5d6cf2a609 chore(release): publish [skip ci]
- @ohif/extension-vtk@1.12.25
 - @ohif/core@2.16.20
 - @ohif/viewer@4.12.28
2022-06-28 11:18:54 +00:00
Davide Punzo 9f813e0ea0 fix: Re IDC2797, fix double parsing of SR qualitative annotations (#2830) 2022-06-28 13:17:16 +02:00
ohif-bot 49de44ba34 chore(release): publish [skip ci]
- @ohif/extension-cornerstone@2.12.11
 - @ohif/extension-vtk@1.12.24
 - @ohif/core@2.16.19
 - @ohif/viewer@4.12.27
2022-06-14 12:14:35 +00:00
Davide Punzo b04e186274 feat IDC #2797: add support for SR qualitative annotation per instance (#2822) 2022-06-14 14:12:27 +02:00
ohif-bot c59d66412f chore(release): publish [skip ci]
- @ohif/extension-vtk@1.12.23
 - @ohif/core@2.16.18
 - @ohif/viewer@4.12.26
2022-04-22 08:54:26 +00:00
Davide Punzo f8b4ccbb59 IDC: Fix undefined return in getDerivedDatasets (#2784) 2022-04-22 10:53:01 +02:00
ohif-bot 5426773ffc chore(release): publish [skip ci]
- @ohif/extension-dicom-tag-browser@0.2.23
 - @ohif/extension-vtk@1.12.22
 - @ohif/core@2.16.17
 - @ohif/ui@1.10.8
 - @ohif/viewer@4.12.25
2022-04-06 16:24:23 +00:00
Davide Punzo a507a00f59 Fix thumbnail loading bar (#2772) 2022-04-06 18:23:02 +02:00
Davide Punzo cedeb756c0 Re IDC #2762: fix thumbnail series type for unsupported SOPClassUID (#2770) 2022-04-06 17:10:00 +02:00
ohif-bot 64385485b6 chore(release): publish [skip ci]
- @ohif/extension-cornerstone@2.12.10
 - @ohif/extension-dicom-html@1.3.21
 - @ohif/extension-dicom-rt@0.7.11
 - @ohif/extension-dicom-segmentation@0.7.11
 - @ohif/extension-dicom-tag-browser@0.2.22
 - @ohif/extension-vtk@1.12.21
 - @ohif/core@2.16.16
 - @ohif/viewer@4.12.24
2022-04-05 07:40:48 +00:00
Davide Punzo 32828d9a6e Re IDC #2765: fix loading of segmentations at first startup (#2768)
Re IDC #2761: fix loading of segmentations

update dcmjs to 0.19.9
2022-04-05 09:39:22 +02:00
ohif-bot b94d4782d1 chore(release): publish [skip ci]
- @ohif/extension-cornerstone@2.12.9
 - @ohif/extension-dicom-html@1.3.20
 - @ohif/extension-dicom-rt@0.7.10
 - @ohif/extension-dicom-segmentation@0.7.10
 - @ohif/extension-dicom-tag-browser@0.2.21
 - @ohif/extension-vtk@1.12.20
 - @ohif/core@2.16.15
 - @ohif/viewer@4.12.23
2022-03-25 11:17:54 +00:00
Igor Octaviano 36b47ce3a3 Merge pull request #2750 from OHIF/igoroctaviano/dcmjs-0.19.8
Update dependency: dcmjs@0.19.8
2022-03-25 08:16:36 -03:00
igoroctaviano fe57c00577 Update dependency: dcmjs@0.19.8
dcmjs
  * @ohif/core: 0.19.7 → 0.19.8
  * @ohif/extension-cornerstone: 0.19.7 → 0.19.8
  * @ohif/extension-dicom-html: 0.19.7 → 0.19.8
  * @ohif/extension-dicom-rt: 0.19.7 → 0.19.8
  * @ohif/extension-dicom-segmentation: 0.19.7 → 0.19.8
  * @ohif/extension-dicom-tag-browser: 0.19.7 → 0.19.8
  * @ohif/extension-vtk: 0.19.7 → 0.19.8
  * @ohif/viewer: 0.19.7 → 0.19.8
2022-03-25 07:45:50 -03:00
ohif-bot 21a3bc2ded chore(release): publish [skip ci]
- @ohif/extension-cornerstone@2.12.8
 - @ohif/extension-dicom-html@1.3.19
 - @ohif/extension-dicom-rt@0.7.9
 - @ohif/extension-dicom-segmentation@0.7.9
 - @ohif/extension-dicom-tag-browser@0.2.20
 - @ohif/extension-vtk@1.12.19
 - @ohif/core@2.16.14
 - @ohif/viewer@4.12.22
2022-03-14 17:19:00 +00:00
Davide Punzo b6b09f22fc IDC2733: update dcmjs to version 0.19.7 (#2745) 2022-03-14 18:17:28 +01:00
ohif-bot 64a4d60e52 chore(release): publish [skip ci]
- @ohif/extension-vtk@1.12.18
 - @ohif/core@2.16.13
 - @ohif/viewer@4.12.21
2022-03-14 16:26:55 +00:00
0ba796c73c fix: Update fetchPaletteColorLookupTableData: LUT Entries shall be 0 if the table entries value is 2^16 (#2395)
Opened issue https://github.com/OHIF/Viewers/issues/2366. 

Following http://dicom.nema.org/medical/Dicom/2018d/output/chtml/part03/sect_C.7.6.3.html#sect_C.7.6.3.1.5, LUT Entries shall be 0 if the table entries value is 2^16.

If numLutEntries is 0, there is no for loop and return empty lut array.

Solution:

const numLutEntries = lutDescriptor[0] ? lutDescriptor[0] : 65536;

This is often used by Philips Ultrasound equipments.

Co-authored-by: Alireza <ar.sedghi@gmail.com>
Co-authored-by: Erik Ziegler <erik.sweed@gmail.com>
2022-03-14 17:25:19 +01:00
ohif-bot cb7fafb1d6 chore(release): publish [skip ci]
- @ohif/extension-vtk@1.12.17
 - @ohif/ui@1.10.7
 - @ohif/viewer@4.12.20
2022-03-14 16:20:31 +00:00
ladeirarodolfo 7164d1a654 fix: remove docz + node version fix on docker (#2743)
* fix: docz uses too outdated packages and current documentation hasnt being updated oftenly. removing it

* fix: node docker version to prevent node req "^12.13.0 || ^14.15.0 || >=16" issue
2022-03-14 17:18:26 +01:00
139 changed files with 2439 additions and 8143 deletions

No files matched your search

+9 -13
View File
@@ -27,7 +27,7 @@ executors:
- image: 'cypress/browsers:node14.15.0-chrome86-ff82'
defaults: &defaults
docker:
- image: cimg/node:14.18
- image: cimg/node:14.19
environment:
TERM: xterm # Enable colors in term
QUICK_BUILD: true
@@ -347,9 +347,8 @@ workflows:
store_artifacts: true
working_directory: platform/viewer
build:
git submodule update --init &&
npx cross-env QUICK_BUILD=true APP_CONFIG=config/e2e.js
yarn run build
git submodule update --init && npx cross-env QUICK_BUILD=true
APP_CONFIG=config/e2e.js yarn run build
start: yarn run test:e2e:serve
spec: 'cypress/integration/common/**/*,cypress/integration/pwa/**/*'
wait-on: 'http://localhost:3000'
@@ -376,9 +375,8 @@ workflows:
store_artifacts: true
working_directory: platform/viewer
build:
git submodule update --init &&
npx cross-env QUICK_BUILD=true APP_CONFIG=config/e2e.js
yarn run build:package
git submodule update --init && npx cross-env QUICK_BUILD=true
APP_CONFIG=config/e2e.js yarn run build:package
start: yarn run test:e2e:serve
spec: 'cypress/integration/common/**/*,cypress/integration/script-tag/**/*'
wait-on: 'http://localhost:3000'
@@ -410,9 +408,8 @@ workflows:
store_artifacts: false
working_directory: platform/viewer
build:
git submodule update --init &&
npx cross-env QUICK_BUILD=true APP_CONFIG=config/e2e.js
yarn run build
git submodule update --init && npx cross-env QUICK_BUILD=true
APP_CONFIG=config/e2e.js yarn run build
# start server --> verify running --> percy + chrome + cypress
command: yarn run test:e2e:dist
cache-key: 'yarn-packages-{{ checksum "yarn.lock" }}'
@@ -490,9 +487,8 @@ workflows:
store_artifacts: false
working_directory: platform/viewer
build:
git submodule update --init &&
npx cross-env QUICK_BUILD=true APP_CONFIG=config/e2e.js
yarn run build
git submodule update --init && npx cross-env QUICK_BUILD=true
APP_CONFIG=config/e2e.js yarn run build
# start server --> verify running --> percy + chrome + cypress
command: yarn run test:e2e:dist
cache-key: 'yarn-packages-{{ checksum "yarn.lock" }}'
-2
View File
@@ -8,7 +8,6 @@ docs/_book
src/version.js
junit.xml
coverage/
.docz/
*.js.map
.yarn
platform/docs/.docusaurus
@@ -38,4 +37,3 @@ screenshots/
# Locize settings
.locize
-10
View File
@@ -22,16 +22,6 @@ mv platform/viewer/dist/* .netlify/www/pwa -v
# Build && Move script output
# yarn run build:package
# Build && Move Docz Output
# Using local yarn install to prevent Gatsby from needing to access
# node_modules above the platform/ui folder
cd platform/ui
yarn install
yarn run build
cd ../..
mkdir -p ./.netlify/www/ui
mv platform/ui/.docz/dist/* .netlify/www/ui -v
# Cache all of the node_module dependencies in
# extensions, modules, and platform packages
yarn run lerna:cache
+2 -2
View File
@@ -21,7 +21,7 @@
# Stage 1: Build the application
# docker build -t ohif/viewer:latest .
FROM node:15.13.0-slim as builder
FROM node:14-slim as builder
RUN mkdir /usr/src/app
WORKDIR /usr/src/app
@@ -42,7 +42,7 @@ COPY yarn.lock /usr/src/app/yarn.lock
RUN apt-get update && apt-get install -y python make g++
# Run the install before copying the rest of the files
RUN yarn config set workspaces-experimental true
RUN yarn install
RUN yarn install --verbose
ENV PATH /usr/src/app/node_modules/.bin:$PATH
ENV QUICK_BUILD true
+64
View File
@@ -3,6 +3,70 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [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)
**Note:** Version bump only for package @ohif/extension-cornerstone
## [2.12.7](https://github.com/OHIF/Viewers/compare/@ohif/extension-cornerstone@2.12.6...@ohif/extension-cornerstone@2.12.7) (2022-02-03)
**Note:** Version bump only for package @ohif/extension-cornerstone
+5 -4
View File
@@ -1,6 +1,6 @@
{
"name": "@ohif/extension-cornerstone",
"version": "2.12.7",
"version": "2.12.15",
"description": "OHIF extension for Cornerstone",
"author": "OHIF",
"license": "MIT",
@@ -35,8 +35,8 @@
"cornerstone-core": "^2.6.1",
"cornerstone-math": "^0.1.9",
"cornerstone-tools": "^6.0.6",
"cornerstone-wado-image-loader": "^4.1.0",
"dcmjs": "0.19.4",
"cornerstone-wado-image-loader": "^4.2.1",
"dcmjs": "0.24.10",
"dicom-parser": "^1.8.11",
"hammerjs": "^2.0.8",
"prop-types": "^15.6.2",
@@ -44,7 +44,8 @@
"react-dom": "^16.8.6",
"react-redux": "^6.0.0",
"react-resize-detector": "^3.4.0",
"redux": "^4.0.1"
"redux": "^4.0.1",
"styled-components": "^5.3.5"
},
"dependencies": {
"@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 (
<OHIFCornerstoneViewportOverlay
{...props}
inconsistencyWarnings={inconsistencyWarnings}
SRLabels={filteredSRLabels}
/>
);
};
@@ -254,7 +264,7 @@ class OHIFCornerstoneViewport extends Component {
imageIdIndex={currentImageIdIndex}
onNewImageDebounced={newImageHandler}
onNewImageDebounceTime={300}
viewportOverlayComponent={warningsOverlay}
viewportOverlayComponent={overlay}
stackPrefetch={this.props.stackPrefetch}
isStackPrefetchEnabled={this.props.isStackPrefetchEnabled}
// ~~ Connected (From REDUX)
@@ -33,6 +33,10 @@
bottom: 140px;
left: 20px;
}
.OHIFCornerstoneViewportOverlay .bottom-left3 {
bottom: 110px;
left: -20px;
}
.OHIFCornerstoneViewportOverlay .bottom-right {
bottom: 20px;
right: 20px;
@@ -1,5 +1,6 @@
import { PureComponent } from 'react';
import React from 'react';
import styled from 'styled-components';
import PropTypes from 'prop-types';
import cornerstone from 'cornerstone-core';
import './OHIFCornerstoneViewportOverlay.css';
@@ -9,13 +10,27 @@ import {
formatDICOMDate,
formatDICOMTime,
formatPN,
getCompression
getCompression,
} from '../utils/formatStudy';
import classNames from 'classnames';
import { Icon } from '@ohif/ui/src/elements/Icon';
import { Tooltip } from '@ohif/ui/src/components/tooltip';
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 {
static propTypes = {
scale: PropTypes.number.isRequired,
@@ -30,11 +45,19 @@ class OHIFCornerstoneViewportOverlay extends PureComponent {
imageId: PropTypes.string.isRequired,
imageIndex: PropTypes.number.isRequired,
stackSize: PropTypes.number.isRequired,
inconsistencyWarnings: PropTypes.array.isRequired
inconsistencyWarnings: PropTypes.array,
SRLabels: PropTypes.array,
};
render() {
const { imageId, scale, windowWidth, windowCenter, inconsistencyWarnings } = this.props;
const {
imageId,
scale,
windowWidth,
windowCenter,
inconsistencyWarnings,
SRLabels,
} = this.props;
if (!imageId) {
return null;
@@ -72,8 +95,11 @@ class OHIFCornerstoneViewportOverlay extends PureComponent {
const { imageIndex, stackSize } = this.props;
const inconsistencyWarningsOn = inconsistencyWarnings && inconsistencyWarnings.length !== 0 ? true : false;
const getWarningContent = (warningList) => {
const inconsistencyWarningsOn =
inconsistencyWarnings && inconsistencyWarnings.length !== 0
? true
: false;
const getWarningContent = warningList => {
if (Array.isArray(warningList)) {
const listedWarnings = warningList.map((warn, index) => {
return <li key={index}>{warn}</li>;
@@ -86,33 +112,100 @@ class OHIFCornerstoneViewportOverlay extends PureComponent {
};
const getWarningInfo = (seriesNumber, inconsistencyWarnings) => {
return(
return (
<React.Fragment>
{inconsistencyWarnings.length != 0 ? (
<OverlayTrigger
key={seriesNumber}
placement="left"
overlay={
<Tooltip
placement="left"
className="in tooltip-warning"
id="tooltip-left"
>
<div className="warningTitle">Series Inconsistencies</div>
<div className="warningContent">{getWarningContent(inconsistencyWarnings)}</div>
</Tooltip>
}
>
<div className={classNames('warning')}>
<span className="warning-icon">
<Icon name="exclamation-triangle" />
</span>
</div>
</OverlayTrigger>
) : (
<React.Fragment></React.Fragment>
{inconsistencyWarnings.length != 0 ? (
<OverlayTrigger
key={seriesNumber}
placement="left"
overlay={
<Tooltip
placement="left"
className="in tooltip-warning"
id="tooltip-left"
>
<div className="warningTitle">Series Inconsistencies</div>
<div className="warningContent">
{getWarningContent(inconsistencyWarnings)}
</div>
</Tooltip>
}
>
<div className={classNames('warning')}>
<span className="warning-icon">
<Icon name="exclamation-triangle" />
</span>
</div>
</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>
<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 className="bottom-left overlay-element">
<div>{seriesNumber >= 0 ? `Ser: ${seriesNumber}` : ''}</div>
@@ -97,6 +97,8 @@ export default class DICOMSRDisplayTool extends BaseTool {
const renderableDataForGraphicType = renderableData[GraphicType];
switch (GraphicType) {
case SCOORD_TYPES.TEXT:
break;
case SCOORD_TYPES.POINT:
this.renderPoint(renderableDataForGraphicType, eventData, options);
break;
@@ -167,20 +169,22 @@ export default class DICOMSRDisplayTool extends BaseTool {
return data.handles.textBox.anchorPoints;
}
draw(context, context => {
drawLinkedTextBox(
context,
element,
data.handles.textBox,
text,
data.handles,
textBoxAnchorPoints,
color,
lineWidth,
0,
true
);
});
if (data.handles.textBox.anchorPoints.length !== 0) {
draw(context, context => {
drawLinkedTextBox(
context,
element,
data.handles.textBox,
text,
data.handles,
textBoxAnchorPoints,
color,
lineWidth,
0,
true
);
});
}
}
// 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 { rows, columns } = image;
const context = getNewContext(eventData.canvasContext.canvas);
const { color, lineWidth } = options;
// Find a suitable length for the image size.
@@ -332,6 +335,8 @@ function _getTextBoxAnchorPointsForRenderableData(renderableData, eventData) {
const renderableDataForGraphicType = renderableData[GraphicType];
switch (GraphicType) {
case SCOORD_TYPES.TEXT:
break;
case SCOORD_TYPES.POINT:
renderableDataForGraphicType.forEach(points => {
anchorPoints = [...anchorPoints, ...points];
@@ -405,7 +410,7 @@ function _getBoundingBoxFromAnchorPoints(anchorPoints) {
minX = x;
}
if (y > maxX) {
if (y > maxY) {
maxY = y;
} else if (y < minY) {
minY = y;
@@ -1,4 +1,5 @@
export default {
TEXT: 'TEXT',
POINT: 'POINT',
MULTIPOINT: 'MULTIPOINT',
POLYLINE: 'POLYLINE',
+8
View File
@@ -3,6 +3,14 @@
All notable changes to this project will be documented in this file.
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)
**Note:** Version bump only for package @ohif/extension-debugging
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@ohif/extension-debugging",
"version": "0.3.2",
"version": "0.3.3",
"description": "OHIF extension for debugging.",
"author": "OHIF",
"license": "MIT",
+33 -48
View File
@@ -89,10 +89,10 @@ const DubugReportModal = ({
Object.keys(viewportSpecificData).forEach(viewportIndex => {
const displaySet = viewportSpecificData[viewportIndex];
const [
referencedDisplaySetsRef,
referencedDisplaySetsCount,
] = _getReferencedSeriesInstanceUIDsString(displaySet, 'SEG');
const [referencedDisplaySetsRef] = _getReferencedSeriesInstanceUIDsString(
displaySet,
'SEG'
);
const [row, column] = _viewportIndexToViewportPosition(
viewportIndex,
@@ -106,10 +106,27 @@ const DubugReportModal = ({
Object.keys(viewportSpecificData).forEach(viewportIndex => {
const displaySet = viewportSpecificData[viewportIndex];
const [
referencedDisplaySetsRef,
referencedDisplaySetsCount,
] = _getReferencedSeriesInstanceUIDsString(displaySet, 'RTSTRUCT');
const [referencedDisplaySetsRef] = _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(
viewportIndex,
@@ -261,11 +278,15 @@ const getLayout = viewports => {
<tr>
<th>ReferencedSEGSeriesInstanceUIDs</th>
</tr>
{getReferencedSEGSeriesInstanceUIDsPerRow(viewports)}
{getReferencedSeriesInstanceUIDsPerRow(viewports, 'SEG')}
<tr>
<th>ReferencedRTSTRUCTSeriesInstanceUIDs</th>
</tr>
{getReferencedRTSTRUCTSeriesInstanceUIDsPerRow(viewports)}
{getReferencedSeriesInstanceUIDsPerRow(viewports, 'RTSTRUCT')}
<tr>
<th>ReferencedSRSeriesInstanceUIDs</th>
</tr>
{getReferencedSeriesInstanceUIDsPerRow(viewports, 'SR')}
</React.Fragment>
);
};
@@ -351,7 +372,7 @@ const _getReferencedSeriesInstanceUIDsString = (displaySet, Modality) => {
return [referencedDisplaySetsRef, referencedDisplaySetsCount];
};
const getReferencedSEGSeriesInstanceUIDsPerRow = viewports => {
const getReferencedSeriesInstanceUIDsPerRow = (viewports, Modality) => {
const { viewportSpecificData, numColumns } = viewports;
// NOTE viewportSpecificData is actually an object with numerical keys.
@@ -360,43 +381,7 @@ const getReferencedSEGSeriesInstanceUIDsPerRow = viewports => {
const [
referencedDisplaySetsRef,
referencedDisplaySetsCount,
] = _getReferencedSeriesInstanceUIDsString(displaySet, 'SEG');
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');
] = _getReferencedSeriesInstanceUIDsString(displaySet, Modality);
const [row, column] = _viewportIndexToViewportPosition(
viewportIndex,
+40
View File
@@ -3,6 +3,46 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [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)
**Note:** Version bump only for package @ohif/extension-dicom-html
## [1.3.18](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-html@1.3.17...@ohif/extension-dicom-html@1.3.18) (2022-02-03)
**Note:** Version bump only for package @ohif/extension-dicom-html
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@ohif/extension-dicom-html",
"version": "1.3.18",
"version": "1.3.23",
"description": "OHIF extension for rendering structured reports to HTML",
"author": "OHIF",
"license": "MIT",
@@ -29,7 +29,7 @@
},
"peerDependencies": {
"@ohif/core": "^0.50.0",
"dcmjs": "0.19.4",
"dcmjs": "0.24.10",
"prop-types": "^15.6.2",
"react": "^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
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',
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',
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);
@@ -32,7 +32,7 @@ const OHIFDicomHtmlSopClassHandler = {
SeriesTime,
} = metadata;
return {
const srDisplaySet = {
plugin: 'html',
Modality: 'SR',
displaySetInstanceUID: utils.guid(),
@@ -49,7 +49,15 @@ const OHIFDicomHtmlSopClassHandler = {
authorizationHeaders,
sopClassUids: sopClassUIDs,
images: series._instances,
isDerived: true,
referencedDisplaySets: [],
};
srDisplaySet.getSourceDisplaySet = function() {
return srDisplaySet.referencedDisplaySets;
};
return srDisplaySet;
},
};
+40
View File
@@ -3,6 +3,46 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [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)
**Note:** Version bump only for package @ohif/extension-dicom-rt
## [0.7.8](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-rt@0.7.7...@ohif/extension-dicom-rt@0.7.8) (2022-02-03)
**Note:** Version bump only for package @ohif/extension-dicom-rt
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@ohif/extension-dicom-rt",
"version": "0.7.8",
"version": "0.7.13",
"description": "OHIF extension for rendering DICOM RTSTRUCTs on top of cornerstone images.",
"author": "OHIF",
"license": "MIT",
@@ -31,7 +31,7 @@
"@ohif/core": "^0.50.0",
"cornerstone-core": "^2.6.1",
"cornerstone-tools": "^6.0.6",
"dcmjs": "0.19.4",
"dcmjs": "0.24.10",
"gl-matrix": "^3.3.0",
"prop-types": "^15.6.2",
"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 cornerstoneTools from 'cornerstone-tools';
import cornerstone from 'cornerstone-core';
@@ -3,6 +3,62 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [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)
**Note:** Version bump only for package @ohif/extension-dicom-segmentation
## [0.7.8](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-segmentation@0.7.7...@ohif/extension-dicom-segmentation@0.7.8) (2022-02-03)
**Note:** Version bump only for package @ohif/extension-dicom-segmentation
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@ohif/extension-dicom-segmentation",
"version": "0.7.8",
"version": "0.7.15",
"description": "OHIF extension for viewing segmentations in the 2D MPR view",
"author": "OHIF",
"license": "MIT",
@@ -31,7 +31,7 @@
"@ohif/core": "^0.50.0",
"cornerstone-core": "^2.6.1",
"cornerstone-tools": "^6.0.6",
"dcmjs": "0.19.4",
"dcmjs": "0.24.10",
"prop-types": "^15.6.2",
"react": "^16.8.6",
"react-dom": "^16.8.6"
@@ -1,4 +1,4 @@
import React, { useState, useEffect } from 'react';
import React, { useState, useEffect, useCallback } from 'react';
import PropTypes from 'prop-types';
import cornerstoneTools from 'cornerstone-tools';
import cornerstone from 'cornerstone-core';
@@ -36,6 +36,7 @@ const { studyMetadataManager } = utils;
* @param {Function} props.onConfigurationChange - Configuration change handler
* @param {Function} props.activeContexts - List of active application contexts
* @param {Function} props.contexts - List of available application contexts
* @param {Function} props.servicesManager - Services manager
* @returns component
*/
const SegmentationPanel = ({
@@ -50,6 +51,7 @@ const SegmentationPanel = ({
onSelectedSegmentationChange,
activeContexts = [],
contexts = {},
servicesManager,
}) => {
const isVTK = () => activeContexts.includes(contexts.VTK);
const isCornerstone = () => activeContexts.includes(contexts.CORNERSTONE);
@@ -59,6 +61,9 @@ const SegmentationPanel = ({
* store with context to make these kind of things less blurry.
*/
const { configuration } = cornerstoneTools.getModule('segmentation');
if (configuration.segsTolerance === undefined) {
configuration.segsTolerance = 1e-2;
}
const DEFAULT_BRUSH_RADIUS = configuration.radius || 10;
/*
@@ -87,6 +92,25 @@ const SegmentationPanel = ({
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 { labelmaps3D, activeLabelmapIndex } = getBrushStackState();
return labelmaps3D[activeLabelmapIndex];
@@ -224,33 +248,15 @@ const SegmentationPanel = ({
)
);
};
}, [
activeIndex,
updateSegmentationComboBox,
viewports,
]);
}, [activeIndex, viewports]);
const updateSegmentationComboBox = e => {
const index = e.detail.activatedLabelmapIndex;
if (index !== -1) {
setState(state => ({ ...state, selectedSegmentation: index }));
} else {
cleanSegmentationComboBox();
}
};
const cleanSegmentationComboBox = () => {
setState(state => ({
...state,
segmentsHidden: [],
segmentNumbers: [],
labelMapList: [],
segmentList: [],
isDisabled: true,
selectedSegmentation: -1,
}));
};
const refreshSegmentations = () => {
const activeViewport = getActiveViewport();
const isDisabled = !activeViewport || !activeViewport.StudyInstanceUID;
@@ -616,7 +622,9 @@ const SegmentationPanel = ({
configuration.outlineWidth = newConfiguration.outlineWidth;
configuration.fillAlphaInactive = newConfiguration.fillAlphaInactive;
configuration.outlineAlphaInactive = newConfiguration.outlineAlphaInactive;
configuration.segsTolerance = newConfiguration.segsTolerance;
onConfigurationChange(newConfiguration);
updateSegDisplaySetsTolerance(configuration.segsTolerance);
refreshViewports();
};
@@ -665,6 +673,7 @@ const SegmentationPanel = ({
configuration={configuration}
onBack={() => setState(state => ({ ...state, showSettings: false }))}
onChange={updateConfiguration}
servicesManager={servicesManager}
/>
);
} else {
@@ -709,7 +718,7 @@ const SegmentationPanel = ({
count={state.segmentList.length}
isVisible={
state.segmentsHidden.filter(isHidden => isHidden === true).length <
state.segmentNumbers.length
state.segmentNumbers.length && state.segmentNumbers.length > 0
}
onVisibilityChange={onVisibilityChangeHandler}
>
@@ -4,7 +4,7 @@ import { Range } from '@ohif/ui';
import './SegmentationSettings.css';
const SegmentationSettings = ({ configuration, onBack, onChange, disabledFields = [] }) => {
const SegmentationSettings = ({ configuration, onBack, onChange, servicesManager, disabledFields = [] }) => {
const [state, setState] = useState({
renderFill: configuration.renderFill,
renderOutline: configuration.renderOutline,
@@ -13,7 +13,8 @@ const SegmentationSettings = ({ configuration, onBack, onChange, disabledFields
outlineAlpha: configuration.outlineAlpha,
outlineWidth: configuration.outlineWidth,
fillAlphaInactive: configuration.fillAlphaInactive,
outlineAlphaInactive: configuration.outlineAlphaInactive
outlineAlphaInactive: configuration.outlineAlphaInactive,
segsTolerance: configuration.segsTolerance,
});
useEffect(() => {
@@ -133,6 +134,40 @@ const SegmentationSettings = ({ configuration, onBack, onChange, disabledFields
)}
</div>
)}
<div className="settings-group" style={{ marginBottom: 15 }}>
<label style={{ margin: '0 15px' }}>
Tolerance:
<input
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);
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,
});
}
}
value={state.segsTolerance}
/>
</label>
</div>
</div>
);
};
@@ -169,11 +204,12 @@ SegmentationSettings.propTypes = {
renderFill: PropTypes.bool.isRequired,
renderOutline: PropTypes.bool.isRequired,
shouldRenderInactiveLabelmaps: PropTypes.bool.isRequired,
fillAlpha: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired, /* TODO: why fillAlpha is string? */
outlineAlpha: 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,
outlineWidth: PropTypes.number.isRequired,
fillAlphaInactive: PropTypes.number.isRequired,
outlineAlphaInactive: PropTypes.number.isRequired,
fillAlphaInactive: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired,
outlineAlphaInactive: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired,
segsTolerance: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired,
}).isRequired,
onBack: PropTypes.func.isRequired,
onChange: PropTypes.func.isRequired,
@@ -62,6 +62,7 @@ export default function getSopClassHandlerModule({ servicesManager }) {
SeriesNumber,
SeriesDescription,
metadata,
tolerance: 1e-2,
};
segDisplaySet.getSourceDisplaySet = function(
@@ -93,7 +94,7 @@ export default function getSopClassHandlerModule({ servicesManager }) {
referencedDisplaySet.SeriesInstanceUID
);
const results = await _parseSeg(segArrayBuffer, imageIds);
const results = await _parseSeg(segArrayBuffer, imageIds, segDisplaySet.tolerance);
if (results === undefined) {
return;
}
@@ -149,10 +150,13 @@ export default function getSopClassHandlerModule({ servicesManager }) {
};
}
function _parseSeg(arrayBuffer, imageIds) {
const skipOverlapping = false;
const tolerance = 1e-2;
const cornerstoneToolsVersion = 4;
function _parseSeg(
arrayBuffer,
imageIds,
tolerance = 1e-2,
skipOverlapping = false,
cornerstoneToolsVersion = 4
) {
return dcmjs.adapters.Cornerstone.Segmentation.generateToolState(
imageIds,
arrayBuffer,
+1 -1
View File
@@ -32,7 +32,6 @@ export default {
const ExtendedSegmentationPanel = props => {
const { activeContexts } = api.hooks.useAppContext();
const onDisplaySetLoadFailureHandler = error => {
LoggerService.error({ error, message: error.message });
UINotificationService.show({
@@ -78,6 +77,7 @@ export default {
onConfigurationChange={onConfigurationChangeHandler}
onSelectedSegmentationChange={onSelectedSegmentationChangeHandler}
onDisplaySetLoadFailure={onDisplaySetLoadFailureHandler}
servicesManager={servicesManager}
/>
);
};
@@ -47,14 +47,23 @@ export default async function setActiveLabelmap(
return labelmapIndex;
}
if (!displaySet.isLoaded) {
if (!displaySet.isLoaded && !displaySet.loadError) {
try {
await displaySet.load(referencedDisplaySet, studies);
} catch (error) {
displaySet.isLoaded = false;
displaySet.loadError = true;
displaySet.segLoadErrorMessagge = error.message;
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;
}
}
+40
View File
@@ -3,6 +3,46 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [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)
**Note:** Version bump only for package @ohif/extension-dicom-tag-browser
## [0.2.19](https://github.com/OHIF/Viewers/compare/@ohif/extension-dicom-tag-browser@0.2.18...@ohif/extension-dicom-tag-browser@0.2.19) (2022-02-03)
**Note:** Version bump only for package @ohif/extension-dicom-tag-browser
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@ohif/extension-dicom-tag-browser",
"version": "0.2.19",
"version": "0.2.24",
"description": "OHIF extension for checking DICOM headers.",
"author": "OHIF",
"license": "MIT",
@@ -28,7 +28,7 @@
},
"peerDependencies": {
"@ohif/core": "^2.6.0",
"dcmjs": "0.19.4",
"dcmjs": "0.24.10",
"react": "^16.8.6"
},
"dependencies": {
@@ -59,7 +59,7 @@ const DicomTagBrowser = ({ displaySets, displaySetInstanceUID }) => {
let metadata;
const isImageStack =
activeDisplaySet instanceof ImageSet &&
activeDisplaySet.isModalitySupported === true;
activeDisplaySet.isSOPClassUIDSupported === true;
let instanceList;
+8
View File
@@ -3,6 +3,14 @@
All notable changes to this project will be documented in this file.
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)
**Note:** Version bump only for package @ohif/extension-lesion-tracker
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@ohif/extension-lesion-tracker",
"version": "0.2.1",
"version": "0.2.2",
"description": "OHIF extension for Lesion Tracker",
"author": "OHIF",
"license": "MIT",
@@ -76,13 +76,13 @@ const additionalFindings = [
const currentCollections = [
{
selectorAction: () => { },
selectorAction: () => {},
maxMeasurements: 3,
groupName: 'Measurements',
measurements: measurements,
},
{
selectorAction: () => { },
selectorAction: () => {},
groupName: 'Additional Findings',
measurements: additionalFindings,
},
@@ -90,13 +90,13 @@ const currentCollections = [
const comparisonColletions = [
{
selectorAction: () => { },
selectorAction: () => {},
maxMeasurements: 3,
groupName: 'Measurements',
measurements: measurements,
},
{
selectorAction: () => { },
selectorAction: () => {},
groupName: 'Additional Findings',
measurements: additionalFindings,
},
@@ -128,7 +128,7 @@ const comparisonTimepoints = [
{
key: 'Comparison',
date: '15-Jun-18',
}
},
];
const MeasurementComparisonTable = () => {
@@ -138,8 +138,8 @@ const MeasurementComparisonTable = () => {
timepoints={comparisonTimepoints}
overallWarnings={overallWarnings}
measurementCollection={comparisonCollections}
onRelabelClick={() => { }}
onEditDescriptionClick={() => { }}
onRelabelClick={() => {}}
onEditDescriptionClick={() => {}}
/>
<div className="generate-report">
<button className="btn btn-primary">Generate Report</button>
+160
View File
@@ -3,6 +3,166 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [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)
**Note:** Version bump only for package @ohif/extension-vtk
## [1.12.18](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.12.17...@ohif/extension-vtk@1.12.18) (2022-03-14)
**Note:** Version bump only for package @ohif/extension-vtk
## [1.12.17](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.12.16...@ohif/extension-vtk@1.12.17) (2022-03-14)
**Note:** Version bump only for package @ohif/extension-vtk
## [1.12.16](https://github.com/OHIF/Viewers/compare/@ohif/extension-vtk@1.12.15...@ohif/extension-vtk@1.12.16) (2022-02-15)
**Note:** Version bump only for package @ohif/extension-vtk
+7 -7
View File
@@ -1,6 +1,6 @@
{
"name": "@ohif/extension-vtk",
"version": "1.12.16",
"version": "1.12.36",
"description": "OHIF extension for VTK.js",
"author": "OHIF",
"license": "MIT",
@@ -34,8 +34,8 @@
"@ohif/ui": "^0.50.0",
"cornerstone-core": "^2.6.1",
"cornerstone-tools": "^6.0.6",
"cornerstone-wado-image-loader": "^4.1.0",
"dcmjs": "0.19.4",
"cornerstone-wado-image-loader": "^4.2.1",
"dcmjs": "0.24.10",
"dicom-parser": "^1.8.11",
"i18next": "^17.0.3",
"i18next-browser-languagedetector": "^3.0.1",
@@ -50,13 +50,13 @@
"dependencies": {
"@babel/runtime": "^7.5.5",
"lodash.throttle": "^4.1.1",
"react-vtkjs-viewport": "^0.14.4"
"react-vtkjs-viewport": "^0.14.5"
},
"devDependencies": {
"@ohif/core": "^2.16.12",
"@ohif/ui": "^1.10.6",
"@ohif/core": "^2.16.30",
"@ohif/ui": "^1.10.10",
"cornerstone-tools": "^6.0.6",
"cornerstone-wado-image-loader": "^4.1.0",
"cornerstone-wado-image-loader": "^4.2.1",
"dicom-parser": "^1.8.11",
"gh-pages": "^2.0.1",
"i18next": "^17.0.3",
+1
View File
@@ -99,6 +99,7 @@
"shader-loader": "^1.3.1",
"start-server-and-test": "^1.10.0",
"style-loader": "^1.0.0",
"styled-components": "^5.3.5",
"stylelint": "^10.1.0",
"stylelint-config-recommended": "^2.2.0",
"stylus": "^0.54.5",
+153
View File
@@ -3,6 +3,159 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [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)
**Note:** Version bump only for package @ohif/core
## [2.16.13](https://github.com/OHIF/Viewers/compare/@ohif/core@2.16.12...@ohif/core@2.16.13) (2022-03-14)
### Bug Fixes
* Update fetchPaletteColorLookupTableData: LUT Entries shall be 0 if the table entries value is 2^16 ([#2395](https://github.com/OHIF/Viewers/issues/2395)) ([0ba796c](https://github.com/OHIF/Viewers/commit/0ba796c73c5254f1bc5ac72c58fd06abf19d7102))
## [2.16.12](https://github.com/OHIF/Viewers/compare/@ohif/core@2.16.11...@ohif/core@2.16.12) (2022-02-15)
**Note:** Version bump only for package @ohif/core
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "@ohif/core",
"version": "2.16.12",
"version": "2.16.30",
"description": "Generic business logic for web-based medical imaging applications",
"author": "OHIF Core Team",
"license": "MIT",
@@ -33,13 +33,13 @@
"peerDependencies": {
"cornerstone-core": "^2.6.1",
"cornerstone-tools": "^6.0.6",
"cornerstone-wado-image-loader": "^4.1.0",
"cornerstone-wado-image-loader": "^4.2.1",
"dicom-parser": "^1.8.11"
},
"dependencies": {
"@babel/runtime": "^7.5.5",
"ajv": "^6.10.0",
"dcmjs": "0.19.4",
"dcmjs": "0.24.10",
"dicomweb-client": "^0.8.3",
"immer": "9.0.12",
"isomorphic-base64": "^1.0.2",
@@ -1,4 +1,5 @@
export default {
TEXT: 'TEXT',
POINT: 'POINT',
MULTIPOINT: 'MULTIPOINT',
POLYLINE: 'POLYLINE',
@@ -8,6 +8,16 @@ const parseSCOORD3D = ({ servicesManager, displaySets }) => {
const { MeasurementService } = servicesManager.services;
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 => {
const firstInstance = srDisplaySet.metadata;
@@ -18,7 +28,7 @@ const parseSCOORD3D = ({ servicesManager, displaySets }) => {
const { ContentSequence } = firstInstance;
srDisplaySet.referencedImages = getReferencedImagesList(ContentSequence);
srDisplaySet.measurements = getMeasurements(ContentSequence, srDisplaySet);
srDisplaySet.measurements = getMeasurements(ContentSequence);
const mappings = MeasurementService.getSourceMappings(
'CornerstoneTools',
'4'
@@ -28,13 +38,6 @@ const parseSCOORD3D = ({ servicesManager, displaySets }) => {
srDisplaySet.isRehydratable = isRehydratable(srDisplaySet, mappings);
srDisplaySet.isLoaded = true;
const imageDisplaySets = displaySets.filter(
ds =>
ds.Modality !== 'SR' &&
ds.Modality !== 'SEG' &&
ds.Modality !== 'RTSTRUCT' &&
ds.Modality !== 'RTDOSE'
);
imageDisplaySets.forEach(imageDisplaySet => {
// Check currently added displaySets and add measurements if the sources exist.
checkIfCanAddMeasurementsToDisplaySet(srDisplaySet, imageDisplaySet);
@@ -127,10 +130,10 @@ const checkIfCanAddMeasurementsToDisplaySet = (
const imageIds = images.map(i => i.getImageId());
const SOPInstanceUIDs = images.map(i => i.SOPInstanceUID);
const colors = new Map();
measurements.forEach(measurement => {
const { coords } = measurement;
coords.forEach(coord => {
coords.forEach((coord, index) => {
if (coord.ReferencedSOPSequence !== undefined) {
const imageIndex = SOPInstanceUIDs.findIndex(
SOPInstanceUID =>
@@ -138,14 +141,50 @@ const checkIfCanAddMeasurementsToDisplaySet = (
coord.ReferencedSOPSequence.ReferencedSOPInstanceUID
);
if (imageIndex > -1) {
if (!srDisplaySet.referencedDisplaySets.includes(imageDisplaySet)) {
srDisplaySet.referencedDisplaySets.push(imageDisplaySet);
}
const imageId = imageIds[imageIndex];
const imageMetadata = images[imageIndex].getData().metadata;
addMeasurement(
measurement,
imageId,
imageMetadata,
imageDisplaySet.displaySetInstanceUID
);
if (coord.GraphicType === 'TEXT') {
const key =
measurement.labels[index].label + measurement.labels[index].value;
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 = {
TrackingUniqueIdentifier: measurement.TrackingUniqueIdentifier,
TrackingIdentifier: measurement.TrackingIdentifier,
renderableData: {},
labels: measurement.labels,
isSRText: measurement.isSRText,
};
measurement.coords.forEach(coord => {
@@ -1,22 +1,14 @@
const getCoordsFromSCOORDOrSCOORD3D = (graphicItem, displaySet) => {
const { ValueType, RelationshipType, 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 getCoordsFromSCOORDOrSCOORD3D = graphicItem => {
const { ValueType, GraphicType, GraphicData } = graphicItem;
const coords = { ValueType, GraphicType, GraphicData };
// ContentSequence has length of 1 as RelationshipType === 'INFERRED FROM'
if (ValueType === 'SCOORD') {
const { ReferencedSOPSequence } = graphicItem.ContentSequence;
coords.ReferencedSOPSequence = ReferencedSOPSequence;
} else if (ValueType === 'SCOORD3D') {
if (graphicItem.ReferencedFrameOfReferenceUID) {
coords.ReferencedFrameOfReferenceSequence = graphicItem.ReferencedFrameOfReferenceUID;
coords.ReferencedFrameOfReferenceSequence =
graphicItem.ReferencedFrameOfReferenceUID;
} else if (graphicItem.ContentSequence) {
const {
ReferencedFrameOfReferenceSequence,
@@ -3,10 +3,7 @@ import getSequenceAsArray from './getSequenceAsArray';
import getMergedContentSequencesByTrackingUniqueIdentifiers from './getMergedContentSequencesByTrackingUniqueIdentifiers';
import processMeasurement from './processMeasurement';
const getMeasurements = (
ImagingMeasurementReportContentSequence,
displaySet
) => {
const getMeasurements = ImagingMeasurementReportContentSequence => {
const ImagingMeasurements = ImagingMeasurementReportContentSequence.find(
item =>
item.ConceptNameCodeSequence.CodeValue ===
@@ -21,25 +18,19 @@ const getMeasurements = (
CodeNameCodeSequenceValues.MeasurementGroup
);
const mergedContentSequencesByTrackingUniqueIdentifiers = getMergedContentSequencesByTrackingUniqueIdentifiers(
/* const mergedContentSequencesByTrackingUniqueIdentifiers = getMergedContentSequencesByTrackingUniqueIdentifiers(
MeasurementGroups
);
);*/
let measurements = [];
Object.keys(mergedContentSequencesByTrackingUniqueIdentifiers).forEach(
trackingUniqueIdentifier => {
const mergedContentSequence =
mergedContentSequencesByTrackingUniqueIdentifiers[
trackingUniqueIdentifier
];
const measurement = processMeasurement(mergedContentSequence, displaySet);
if (measurement) {
measurements.push(measurement);
}
MeasurementGroups.forEach(MeasurementGroup => {
const contentSequence = MeasurementGroup.ContentSequence;
const measurement = processMeasurement(contentSequence);
if (measurement) {
measurements.push(measurement);
}
);
});
return measurements;
};
@@ -10,7 +10,7 @@ const getReferencedImagesList = ImagingMeasurementReportContentSequence => {
CodeNameCodeSequenceValues.ImageLibrary
);
if (!ImageLibrary.ContentSequence) {
if (!ImageLibrary || !ImageLibrary.ContentSequence) {
return referencedImages;
}
@@ -22,6 +22,10 @@ const getReferencedImagesList = ImagingMeasurementReportContentSequence => {
CodeNameCodeSequenceValues.ImageLibraryGroup
);
if (!ImageLibraryGroup || !ImageLibraryGroup.ContentSequence) {
return referencedImages;
}
getSequenceAsArray(ImageLibraryGroup.ContentSequence).forEach(item => {
const { ReferencedSOPSequence } = item;
if (ReferencedSOPSequence) {
@@ -57,6 +57,10 @@ const getRenderableData = (
// https://dicom.innolitics.com/ciods/procedure-log/sr-document-content/00700023
switch (GraphicType) {
case SCOORD_TYPES.TEXT:
renderableData = [{ x: GraphicData[0], y: GraphicData[1] }];
break;
case SCOORD_TYPES.POINT:
renderableData = [];
@@ -1,16 +1,16 @@
import processTID1410Measurement from './processTID1410Measurement';
import processNonGeometricallyDefinedMeasurement from './processNonGeometricallyDefinedMeasurement';
const processMeasurement = (mergedContentSequence, displaySet) => {
const processMeasurement = contentSequence => {
if (
mergedContentSequence.some(
contentSequence.some(
group => group.ValueType === 'SCOORD' || group.ValueType === 'SCOORD3D'
)
) {
return processTID1410Measurement(mergedContentSequence, displaySet);
return processTID1410Measurement(contentSequence);
}
return processNonGeometricallyDefinedMeasurement(mergedContentSequence);
return processNonGeometricallyDefinedMeasurement(contentSequence);
};
export default processMeasurement;
@@ -1,31 +1,40 @@
import getLabelFromMeasuredValueSequence from './getLabelFromMeasuredValueSequence';
import getCoordsFromSCOORDOrSCOORD3D from './getCoordsFromSCOORDOrSCOORD3D';
import { CodeNameCodeSequenceValues, CodingSchemeDesignators } from '../enums';
import SCOORD_TYPES from '../constants/scoordTypes';
const CORNERSTONE_FREETEXT_CODE_VALUE = 'CORNERSTONEFREETEXT';
const processNonGeometricallyDefinedMeasurement = mergedContentSequence => {
const NUMContentItems = mergedContentSequence.filter(
const processNonGeometricallyDefinedMeasurement = contentSequence => {
const NUMContentItems = contentSequence.filter(
group => group.ValueType === 'NUM'
);
const UIDREFContentItem = mergedContentSequence.find(
const CODEContentItems = contentSequence.filter(
group => group.ValueType === 'CODE'
);
const UIDREFContentItem = contentSequence.find(
group => group.ValueType === 'UIDREF'
);
const TrackingIdentifierContentItem = mergedContentSequence.find(
const IMAGEContentItem = contentSequence.find(
group => group.ValueType === 'IMAGE'
);
const TrackingIdentifierContentItem = contentSequence.find(
item =>
item.ConceptNameCodeSequence.CodeValue ===
CodeNameCodeSequenceValues.TrackingIdentifier
);
const Finding = mergedContentSequence.find(
const Finding = contentSequence.find(
item =>
item.ConceptNameCodeSequence.CodeValue ===
CodeNameCodeSequenceValues.Finding
);
const FindingSites = mergedContentSequence.filter(
const FindingSites = contentSequence.filter(
item =>
item.ConceptNameCodeSequence.CodingSchemeDesignator ===
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;
};
@@ -5,14 +5,14 @@ import { RELATIONSHIP_TYPE, CodeNameCodeSequenceValues } from '../enums';
/**
* TID 1410 Planar ROI Measurements and Qualitative Evaluations.
*
* @param {*} mergedContentSequence
* @param {*} contentSequence
* @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,
// 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'
);
@@ -23,24 +23,24 @@ const processTID1410Measurement = (mergedContentSequence, displaySet) => {
return;
}
const UIDREFContentItem = mergedContentSequence.find(
const UIDREFContentItem = contentSequence.find(
group => group.ValueType === 'UIDREF'
);
const TrackingIdentifierContentItem = mergedContentSequence.find(
const TrackingIdentifierContentItem = contentSequence.find(
item =>
item.ConceptNameCodeSequence.CodeValue ===
CodeNameCodeSequenceValues.TrackingIdentifier
);
const NUMContentItems = mergedContentSequence.filter(
const NUMContentItems = contentSequence.filter(
group => group.ValueType === 'NUM'
);
const measurement = {
loaded: false,
labels: [],
coords: [getCoordsFromSCOORDOrSCOORD3D(graphicItem, displaySet)],
coords: [getCoordsFromSCOORDOrSCOORD3D(graphicItem)],
TrackingUniqueIdentifier: UIDREFContentItem.UID,
TrackingIdentifier: TrackingIdentifierContentItem.TextValue,
};
@@ -21,16 +21,32 @@ const parseDicomStructuredReport = (
displaySets,
external
) => {
if (external && external.servicesManager) {
parseSCOORD3D({ servicesManager: external.servicesManager, displaySets });
}
// Get the dicom data as an Object
const dicomData = dcmjs.data.DicomMessage.readFile(part10SRArrayBuffer);
const dataset = dcmjs.data.DicomMetaDictionary.naturalizeDataset(
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;
let storedMeasurementByToolType;
@@ -38,11 +54,12 @@ const parseDicomStructuredReport = (
storedMeasurementByToolType = MeasurementReport.generateToolState(dataset);
} catch (error) {
const seriesDescription = dataset.SeriesDescription || '';
LogManager.publish(LogManager.EVENTS.OnLog, {
LoggerService.error({ error, message: error.message });
UINotificationService.show({
title: `Failed to parse ${seriesDescription} measurement report`,
type: 'warning',
message: error.message || '',
notify: true,
message: error.message,
type: 'error',
autoClose: false,
});
return;
}
+2 -1
View File
@@ -67,12 +67,13 @@ export class HotkeysManager {
UINotificationService,
LoggerService,
} = this._servicesManager.services;
const message = 'Erro while setting hotkeys';
const message = 'Error while setting hotkeys';
LoggerService.error({ error, message });
UINotificationService.show({
title: 'Hotkeys Manager',
message,
type: 'error',
autoClose: false,
});
}
}
@@ -401,7 +401,7 @@ class StackLoadingListener extends BaseLoadingListener {
const totalFramesCount = this.stack.imageIds.length;
const loadedFramesCount = this.loadedCount;
const loadingFramesCount = totalFramesCount - loadedFramesCount;
const percentComplete = Math.round(
const percentComplete = Math.ceil(
(loadedFramesCount / totalFramesCount) * 100
);
const progressId = this._getProgressId();
@@ -469,8 +469,8 @@ class StudyLoadingListener {
// TODO: Make this work for plugins
if (!stack) {
console.warn('Skipping adding displaySet to StudyLoadingListener');
console.warn(displaySet);
//console.warn('Skipping adding displaySet to StudyLoadingListener');
//console.warn(displaySet);
return;
}
@@ -3,12 +3,12 @@ import DICOMWeb from './../../DICOMWeb';
import ImageSet from './../ImageSet';
import { InstanceMetadata } from './InstanceMetadata';
import { Metadata } from './Metadata';
import OHIFError from '../OHIFError';
import { SeriesMetadata } from './SeriesMetadata';
// - createStacks
import { api } from 'dicomweb-client';
// - createStacks
import { isImage } from '../../utils/isImage';
import { naturalizeSOPClassUID } from '../../utils/naturalizeSOPClassUID';
import {
isDisplaySetReconstructable,
isSpacingUniform,
@@ -169,8 +169,12 @@ class StudyMetadata extends Metadata {
!isImage(instance.getTagValue('SOPClassUID')) &&
!instance.getTagValue('Rows')
) {
// we set an empty display and we add a isModalitySupported variable to
// print a warning that the modality is not supported in the thumbnail.
// we set an empty display and we add a isSOPClassUIDSupported variable to
// 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 seriesData = series.getData();
displaySet.setAttributes({
@@ -188,7 +192,10 @@ class StudyMetadata extends Metadata {
InstanceNumber: instance.getTagValue('InstanceNumber'), // Include the instance number
AcquisitionDatetime: instance.getTagValue('AcquisitionDateTime'), // Include the acquisition datetime
isReconstructable: false,
isModalitySupported: false,
isSOPClassUIDSupported: false,
SOPClassUIDNaturalized: naturalizeSOPClassUID(
instance.getTagValue('SOPClassUID')
),
metadata: instance.getData().metadata,
});
@@ -268,6 +275,8 @@ class StudyMetadata extends Metadata {
const otherDisplaySets = allDisplaySets.filter(
ds =>
ds &&
derivatedDisplaySet &&
ds.displaySetInstanceUID !== derivatedDisplaySet.displaySetInstanceUID
);
@@ -333,10 +342,38 @@ class StudyMetadata extends Metadata {
if (referencedSeriesInstanceUID) {
filteredDerivedDisplaySets = filteredDerivedDisplaySets.filter(
displaySet => {
return (
StudyMetadata.getReferencedDisplaySet(displaySet, [this])
.SeriesInstanceUID === referencedSeriesInstanceUID
const referencedDisplaySet = StudyMetadata.getReferencedDisplaySet(
displaySet,
[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;
}
imageSet.isModalitySupported = true;
imageSet.isSOPClassUIDSupported = true;
return imageSet;
};
@@ -6,7 +6,8 @@ export const dicomSRDisplayTool = {
options: {
measurementTable: {
displayFunction: data => {
return `(SR) ${data.lesionNamingNumber ||
return `(SR) ${data.TrackingIdentifier ||
data.lesionNamingNumber ||
data.measurementNumber ||
data.text ||
''}`;
@@ -420,6 +420,10 @@ class MeasurementService {
throw new Error('No source definition provided.');
}
if (definition == 'stack' || definition == 'stackPrefetch') {
return;
}
const sourceInfo = this._getSourceInfo(source);
if (!this._sourceHasMappings(source)) {
@@ -19,17 +19,4 @@ describe('retrieveStudyMetadata.js', () => {
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);
});
});
@@ -102,6 +102,35 @@ async function makeSOPInstance(server, study, instance) {
SOPInstanceUID,
} = 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];
if (!series) {
+2 -2
View File
@@ -25,7 +25,7 @@ function createAndAddStack(stackMap, study, displaySet, stackUpdatedCallbacks) {
const imageIds = [];
let imageId;
displaySet.images.forEach((instance, imageIndex) => {
images.forEach((instance, imageIndex) => {
const image = instance.getData();
const metaData = {
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,
};
const naturalizedInstance = instance.getData().metadata;
const naturalizedInstance = image.metadata;
const NumberOfFrames = naturalizedInstance.NumberOfFrames;
if (NumberOfFrames > 1) {
@@ -49,7 +49,12 @@ import studyMetadataManager from './studyMetadataManager';
* @param {object} snackbar
* @returns void
*/
async function loadAndCacheDerivedDisplaySets(referencedDisplaySet, studies, logger, snackbar) {
async function loadAndCacheDerivedDisplaySets(
referencedDisplaySet,
studies,
logger,
snackbar
) {
const { StudyInstanceUID, SeriesInstanceUID } = referencedDisplaySet;
const promises = [];
const studyMetadata = studyMetadataManager.get(StudyInstanceUID);
@@ -80,60 +85,111 @@ async function loadAndCacheDerivedDisplaySets(referencedDisplaySet, studies, log
});
// For each type, see if any are loaded, if not load the most recent.
await Promise.all(Object.keys(displaySetsPerModality).map(async (key) => {
const displaySets = displaySetsPerModality[key];
await Promise.all(
Object.keys(displaySetsPerModality).map(async key => {
const displaySets = displaySetsPerModality[key];
const isLoaded = displaySets.some(displaySet => displaySet.isLoaded);
if (isLoaded) {
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;
const isLoaded = displaySets.some(displaySet => displaySet.isLoaded);
if (isLoaded) {
return;
}
});
try {
if (recentDisplaySet.hasOwnProperty('getSourceDisplaySet') &&
typeof recentDisplaySet.getSourceDisplaySet === 'function') {
await recentDisplaySet.getSourceDisplaySet(studies);
} else {
await recentDisplaySet.load(referencedDisplaySet, studies);
if (displaySets.some(displaySet => displaySet.loadError)) {
return;
}
} 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,
// 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 (
recentDisplaySet.hasOwnProperty('getSourceDisplaySet') &&
typeof recentDisplaySet.getSourceDisplaySet === 'function'
) {
if (recentDisplaySet.Modality === 'SEG' && logger) {
const onDisplaySetLoadFailureHandler = error => {
logger.error({ error, message: error.message });
snackbar.show({
title: 'DICOM Segmentation Loader',
message: error.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
* that depends on derived display sets to be loaded.
* (Implement pubsub for better tracking of derived display sets)
*/
* 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('deriveddisplaysetsloadedandcached');
document.dispatchEvent(event);
};
}
export default loadAndCacheDerivedDisplaySets;
@@ -113,7 +113,7 @@ const _paletteColorCache = {
};
function _getPaletteColor(server, paletteColorLookupTableData, lutDescriptor) {
const numLutEntries = lutDescriptor[0];
const numLutEntries = lutDescriptor[0] ? lutDescriptor[0] : 65536;
const bits = lutDescriptor[2];
const arrayBufferToPaletteColorLUT = arraybuffer => {
@@ -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
View File
@@ -1,8 +1,5 @@
####
##
## We need to pin WebPack's version for docz to work. See this issue:
## https://github.com/pedronauck/docz/issues/704#issuecomment-480295032
##
## react-scripts checks that dependency versions match before running
## Our top level webpack version causes the preflight check to fail
## This prevents that check
+35
View File
@@ -3,6 +3,41 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [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)
### Bug Fixes
* remove docz + node version fix on docker ([#2743](https://github.com/OHIF/Viewers/issues/2743)) ([7164d1a](https://github.com/OHIF/Viewers/commit/7164d1a654571af99c22d2684e56cbdde05b2222))
## [1.10.6](https://github.com/OHIF/Viewers/compare/@ohif/ui@1.10.5...@ohif/ui@1.10.6) (2022-02-03)
**Note:** Version bump only for package @ohif/ui
-75
View File
@@ -1,75 +0,0 @@
/**
* Docz Configuration File:
* https://www.docz.site/docs/project-configuration
*/
import { css } from "docz-plugin-css";
export default {
dest: "example/build",
public: "/public",
wrapper: "src/__docs__/wrapper",
indexHtml: "src/__docs__/index.html",
// Limited support for importing `.styl` files
codeSandbox: false,
// https://github.com/pedronauck/docz/pull/849/files
// Because we re-export using `index.js` files
notUseSpecifiers: true,
menu: [
"Introduction",
"Getting Started",
{ name: "Elements", menu: ["Form / Select"] },
{
name: "Components",
menu: [
"CINE Dialog",
"Layout Button",
"Measurement Table",
"Overlay Trigger",
"Quick Switch",
"Rounded Button Group",
"Select Tree",
"Simple Dialog",
"Study Browser",
"Study List",
"Table List",
"Toolbar Section",
"About Modal",
"User Preferences Modal"
]
},
"Styling & Theming",
"Translating",
"Compatibility"
],
// Rollup Aliases?
// https://github.com/pedronauck/docz/issues/373
plugins: [
// CSS
css(),
// Stylus
css({
preprocessor: "stylus",
cssmodules: false
})
],
// `docz` uses file-loader to pull in SVGs. This kills our icons before
// They can be picked up by our `babel-plugin-inline-react-svg` dependency
// You can see where it's configured here:
// https://github.com/pedronauck/docz/blob/f72624d0aa5231dab17e1770e8d36be920f590a2/core/docz-core/src/bundler/loaders.ts#L135-L144
// How we delete our rule:
// https://github.com/neutrinojs/webpack-chain/issues/48
onCreateWebpackChain: config => {
config.module.rules.delete("svg");
},
modifyBabelRc: babelrc => {
const newBabelRc = {
...babelrc,
plugins: [
...babelrc.plugins,
require.resolve("babel-plugin-inline-react-svg")
]
};
return newBabelRc;
}
};
-25
View File
@@ -1,25 +0,0 @@
const path = require('path');
// ~~ Plugins
const PnpWebpackPlugin = require(`pnp-webpack-plugin`); // Required until Webpack@5
exports.onCreateWebpackConfig = args => {
args.actions.setWebpackConfig({
node: { fs: 'empty' },
resolve: {
plugins: [PnpWebpackPlugin],
// Note the '..' in the path because docz gatsby project lives in the '.docz' directory
modules: [
// platform/ui
path.resolve(__dirname, '../node_modules'),
// .docz
'node_modules',
],
// resolve: {
// symlinks: true,
// },
},
resolveLoader: {
plugins: [PnpWebpackPlugin.moduleLoader(module)],
},
});
};
+2 -7
View File
@@ -1,6 +1,6 @@
{
"name": "@ohif/ui",
"version": "1.10.6",
"version": "1.10.10",
"description": "A set of React components for Medical Imaging Viewers",
"author": "OHIF Contributors",
"license": "MIT",
@@ -20,9 +20,8 @@
"README.md"
],
"scripts": {
"dev": "docz dev",
"dev": "echo \"No dev script yet\" && exit 0",
"dev:ui": "yarn run dev",
"docs": "docz build",
"build": "cross-env NODE_ENV=production webpack --config .webpack/webpack.prod.js",
"build:package": "yarn run build",
"prepublishOnly": "yarn run build",
@@ -64,10 +63,6 @@
"devDependencies": {
"@mdx-js/tag": "^0.20.3",
"autoprefixer": "^9.6.0",
"docz": "1.2.0",
"docz-core": "1.2.0",
"docz-plugin-css": "0.11.0",
"docz-theme-default": "1.2.0",
"lodash.clonedeep": "^4.5.0",
"react-powerplug": "1.0.0"
}
-17
View File
@@ -1,17 +0,0 @@
import React, { Fragment } from 'react';
import { Link } from 'docz';
import PropTypes from 'prop-types';
const NameSpace = ({ name }) => (
<Fragment>
The namespace used on this component was: <pre>{name}</pre> Check the{' '}
<Link to="/translating#how-to-translate-components">translation docs</Link>{' '}
to see how to override it.
</Fragment>
);
NameSpace.propTypes = {
name: PropTypes.string.isRequired,
};
export default NameSpace;
Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

@@ -1,15 +0,0 @@
---
name: Compatibility
route: /compatibility
---
# Compatibility
## Browser Support
`@ohif/ui` relies on
[browsers supported by Styled Components](https://www.styled-components.com/docs/faqs#which-browsers-are-supported)
or browsers supported by emotion.
Supported Browsers: IE11, IE 9+ (with Map + Set polyfills), Chrome, Firefox (and
derivatives), Edge, and Safari.
Whitespace-only changes.
@@ -1,13 +0,0 @@
---
name: Docs Settings
route: /docs-settings
---
import LanguageSwitcher from '../components/languageSwitcher';
# Components Language
You can change the language of the components displayed in this documentation by
choosing an option in the selector below:
<LanguageSwitcher />
@@ -1,76 +0,0 @@
---
name: Getting Started
route: /getting-started
---
# Getting Started
## Installation
> This component library is pre- v1.0. All realeases until a v1.0 have the
> possibility of introducing breaking changes. Please depend on an "exact"
> version in your projects to prevent issues caused by loose versioning.
Install `@ohif/ui` from npm:
```shell
// with npm
npm i @ohif/ui --save-exact
// with yarn
yarn add @ohif/ui --exact
```
## Usage
There is some setup required for `@ohif/ui`. We are working to eliminate these
requirements with the goal of providing ready to use components.
### External Dependencies
We have tried to keep the number of external dependencies limited to make setup
as easy as possible. You will, however, need to include either the default font
or the font you set for your theme:
```js
// Google Fonts, Roboto
'https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700&display=swap';
```
`@ohif/ui` also looks for theme CSS variabled defined on `:root`. You can find a
list of variables [on the theming page](/styling-and-theming)
### Import & Use
You can use components in your React app:
```js
import React, { Component } from 'react';
import { LayoutButton } from '@ohif/ui';
class Example extends Component {
constructor(props) {
super(props);
this.state = {
selectedCell: {
className: 'hover',
col: 1,
row: 1,
},
};
}
render() {
return (
<LayoutButton
selectedCell={this.state.selectedCell}
onChange={cell => this.setState({ selectedCell: cell })}
/>
);
}
}
```
> NOTE: It is heavily recommended that you use a CSS reset or normalize. As well
> as the following style: `* { Box-sizing: Border-box }`
-83
View File
@@ -1,83 +0,0 @@
<!DOCTYPE html>
<html lang="{{ lang }}">
<head>
<meta charset="UTF-8">
<meta name="description" content="{{ description }}">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>{{ title }}</title>
<!-- Latest compiled and minified CSS -->
<link rel="stylesheet" href="/public/docz.css">
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700&display=swap">
<style>
/* "Tide" theme */
/* TODO:
- Convert the other themes to CSS Variables
- Allow user to set default CSS theme in JS config
- Export the themes as identical JS objects
- For theme switcher, update root-level CSS properties via JS (https://medium.com/@_bengarrison/accessing-and-modifying-css-variables-with-javascript-2ccb735bbff0)
*/
:root {
/* Interface UI Colors */
--default-color: #9CCEF9;
--hover-color: #ffffff;
--active-color: #20A5D6;
--ui-border-color: #44626F;
--ui-border-color-dark: #3C5D80;
--ui-border-color-active: #00a4d9;
--primary-background-color: #000000;
--box-background-color: #3E5975;
--text-primary-color: #ffffff;
--text-secondary-color: #91B9CD;
--input-background-color: #2c363f;
--input-placeholder-color: #d3d3d3;
--table-hover-color: #2c363f;
--table-text-primary-color: #ffffff;
--table-text-secondary-color: #91b9cd;
--large-numbers-color: #6FBDE2;
--state-error: #FFCCCC;
--state-error-border: #FFCCCC;
--state-error-text: #FFCCCC;
/* Common palette */
--ui-yellow: #E29E4A;
--ui-sky-blue: #6FBDE2;
/* State palette */
--ui-state-error: #FFCCCC;
--ui-state-error-border: #993333;
--ui-state-error-text: #661111;
--ui-gray-lighter: #436270;
--ui-gray-light: #516873;
--ui-gray: #263340;
--ui-gray-dark: #16202B;
--ui-gray-darker: #151A1F;
--ui-gray-darkest: #14202A;
--calendar-day-color: #d3d3d3;
--calendar-day-border-color: #d3d3d3;
--calendar-day-active-hover-background-color: #516873;
--calendar-main-color: #263340;
--viewport-border-thickness: 1px;
}
body {
font-family: 'Roboto', sans-serif !important;
}
</style>
{{ head }}
</head>
<body>
<div id="root" />
{{ footer }}
</body>
</html>
-76
View File
@@ -1,76 +0,0 @@
---
name: Introduction
route: /
---
import { Playground, Props } from 'docz';
import { State } from 'react-powerplug';
import { TableList, TableListItem } from './../index.js';
# Introduction
**@ohif/ui is a collection of components and utilities** that power OHIF's
[zero-footprint DICOM viewer](https://github.com/OHIF/Viewers)
([demo][demo-url]). We maintain them as a separate component library to:
- Decouple presentation from business logic
- Test and develop components in isolation
- Provide well documented, reusable components
- Aid rapid application development for context specific viewers
## Quick Example
> This component library is pre- v1.0. All realeases until a v1.0 have the
> possibility of introducing breaking changes. Please depend on an "exact"
> version in your projects to prevent issues caused by loose versioning.
```js
// Add '@ohif/ui' as a dependency
yarn add @ohif/ui
// Import and use components
import { TableList } from '@ohif/ui'
```
<Playground>
<State
initial={{
selectedIndex: null,
listItems: [
{ label: 'Brain Left' },
{ label: 'Brain Right' },
{ label: 'Heart Left' },
{ label: 'Heart Right' },
],
}}
>
{({ state, setState }) => (
<TableList headerTitle="Example Table List Header">
{state.listItems.map((item, index) => {
return (
<TableListItem
key={`item_${index}`}
itemClass={state.selectedIndex === index ? 'selected' : ''}
itemIndex={index}
onItemClick={() => alert('item clicked')}
>
<label>{item.label}</label>
</TableListItem>
);
})}
</TableList>
)}
</State>
</Playground>
## FAQ
...
<!--
Links
-->
<!-- prettier-ignore-start -->
[demo-url]: https://docs.ohif.org/demo/
<!-- prettier-ignore-end -->
@@ -1,56 +0,0 @@
---
name: Styling & Theming
route: /styling-and-theming
---
# Styling & Theming
```css
:root {
/* Interface UI Colors */
--default-color: #9ccef9;
--hover-color: #ffffff;
--active-color: #20a5d6;
--ui-border-color: #44626f;
--ui-border-color-dark: #3c5d80;
--ui-border-color-active: #00a4d9;
--primary-background-color: #000000;
--box-background-color: #3e5975;
--text-primary-color: #ffffff;
--text-secondary-color: #91b9cd;
--input-background-color: #2c363f;
--input-placeholder-color: #d3d3d3;
--table-hover-color: #2c363f;
--table-text-primary-color: #ffffff;
--table-text-secondary-color: #91b9cd;
--large-numbers-color: #6fbde2;
--state-error: #ffcccc;
--state-error-border: #ffcccc;
--state-error-text: #ffcccc;
/* Common palette */
--ui-yellow: #e29e4a;
--ui-sky-blue: #6fbde2;
/* State palette */
--ui-state-error: #ffcccc;
--ui-state-error-border: #993333;
--ui-state-error-text: #661111;
--ui-gray-lighter: #436270;
--ui-gray-light: #516873;
--ui-gray: #263340;
--ui-gray-dark: #16202b;
--ui-gray-darker: #151a1f;
--ui-gray-darkest: #14202a;
--calendar-day-color: #d3d3d3;
--calendar-day-border-color: #d3d3d3;
--calendar-day-active-hover-background-color: #516873;
--calendar-main-color: #263340;
--viewport-border-thickness: 1px;
}
```
-87
View File
@@ -1,87 +0,0 @@
---
name: Translating
route: /translating
---
# Translating
**React Viewerbase** supports internationalization using
[i18next](https://www.i18next.com/) through the npm package
[@ohif/i18n](https://www.npmjs.com/package/@ohif/i18n), where is the main
instance of i18n containing several languages and tools.
## How to translate components
All you need is to use the internal custom superset of
[withTranslation HOC](https://react.i18next.com/latest/withtranslation-hoc),
available at `src/utils/LanguageProvider.js`
e.g.:
```js
import React from 'react';
import { withTranslation } from '../utils/LanguageProvider.js';
function MyComponent({ t }) {
return <p>{t('my translated text')}</p>;
}
export default withTranslation('MyNameSpace')(MyComponent);
```
---
## Namespaces
For further information about namespaces and how they work, please visit
[OHIF Namespaces docs](https://docs.ohif.org/viewer/internationalization.html#namespaces).
### How to get right the Namespace of a `@ohif/ui` component?
Check the component's page in this site, the Namespace information should be on
the bottom line.
Also, a nameSpace is defined when exporting a component, this information can be
found on exports trough the code, in case of missing in docs.
E.g.
```
export default withTranslation('TheNamespaceYouAreLookingFor')(TheComponentYouWantToUse);
```
### How to use another Namespace inside the current Namespace?
i18next provides a parsing feature able to get translations strings from any
Namespace, like this following example getting data from `InnerSpace` Namespace:
```
$t(InnerSpace:Reset)
export default withTranslation('OuterSpace')(TheComponent);
```
---
## I18next Provider
**@ohif/ui** pass down the `t` function trough the custom
[withTranslation HOC](https://github.com/OHIF/Viewers/blob/master/platform/ui/src/utils/LanguageProvider.js).
---
## Extending and Overriding languages
[How to Extend languages with @ohif/i18n](https://docs.ohif.org/viewer/internationalization.html#extending-languages-in-ohifi18n)
## Changing the language
The language choosing method is the same implemented in
[OHIF Viewers](https://docs.ohif.org/viewer/internationalization.html#changing-the-language)
> You can also change the language of the components displayed in this
> documentation by choosing an option in the
> [Docs Settings](https://react.ohif.org/docs-settings) section.
## Debugging translations
[How to Debug @ohif/i18n](https://docs.ohif.org/viewer/internationalization.html#debugging-translations)
-22
View File
@@ -1,22 +0,0 @@
import React from 'react';
import PropTypes from 'prop-types';
import LanguageSwitcher from '../components/languageSwitcher';
import './wrapper.styl';
const Wrapper = ({ children }) => (
<React.Fragment>
<div className="sidebarLanguageSwitcher">
<strong>Display components in:</strong>
<LanguageSwitcher />
</div>
{children}
</React.Fragment>
);
Wrapper.propTypes = {
children: PropTypes.node.isRequired,
};
export default Wrapper;
-15
View File
@@ -1,15 +0,0 @@
[class*="Sidebar__Menus"]
@media (min-width: 1120px)
margin-top: 80px
.sidebarLanguageSwitcher
position: fixed
top: 155px
left 0
z-index: 1049
padding: 0 24px 20px
border-bottom: 1px dotted #CED4DE
background: #F5F6F7
transition: all 200ms ease
@media (max-width: 1119px)
left -100%
@@ -1,42 +0,0 @@
---
name: Checkbox
menu: Elements
route: /components/checkbox
---
import { Playground, Props } from 'docz'
import { State } from 'react-powerplug'
import { Checkbox } from './../index'
import NameSpace from '../../../__docs__/NameSpace'
# Checkbox
## Basic usage
<Playground>
<State
initial={{
checked : false,
label : 'Sample check'
}}
>
{({ state, setState }) => (
<React.Fragment>
<div>
<pre>{JSON.stringify(state, null, 2)}</pre>
</div>
<div style={{ maxWidth: '300px', margin: '0 auto' }}>
<Checkbox {...state} />
</div>
</React.Fragment>
)}
</State>
</Playground>
## API
<Props of={Checkbox} />
## Translation Namespace
<NameSpace name="Checkbox" />
@@ -1,57 +0,0 @@
---
name: CINE Dialog
menu: Components
route: /components/cine-dialog
---
import { Playground, Props } from 'docz'
import { State } from 'react-powerplug'
import { CineDialog } from './../index'
import NameSpace from '../../../__docs__/NameSpace'
# CINE Dialog
## Basic usage
<Playground>
<State
initial={{
isPlaying: false,
cineFrameRate: 24,
isLoopEnabled: true,
lastChange: '',
}}
>
{({ state, setState }) => (
<React.Fragment>
<div>
<pre>{JSON.stringify(state, null, 2)}</pre>
</div>
<div style={{ maxWidth: '300px', margin: '0 auto' }}>
<CineDialog
{...state}
onClickSkipToStart={() =>
setState({ lastChange: 'Clicked SkipToStart' })
}
onClickSkipToEnd={() =>
setState({ lastChange: 'Clicked SkipToEnd' })
}
onClickNextButton={() => setState({ lastChange: 'Clicked Next' })}
onClickBackButton={() => setState({ lastChange: 'Clicked Back' })}
onLoopChanged={value => setState({ isLoopEnabled: value })}
onFrameRateChanged={value => setState({ cineFrameRate: value })}
onPlayPauseChanged={() => setState({ isPlaying: !state.isPlaying })}
/>
</div>
</React.Fragment>
)}
</State>
</Playground>
## API
<Props of={CineDialog} />
## Translation Namespace
<NameSpace name="CineDialog" />
@@ -1,42 +0,0 @@
---
name: About Modal
menu: Components
route: /components/about-modal
---
import { Playground, Props } from 'docz'
import { State } from 'react-powerplug'
import { AboutContent } from './../index.js'
# About Modal
## Basic usage
<Playground>
<State initial={{
isOpen: false
}}>
{({ state, setState }) => (
<React.Fragment>
<button
className="btn btn-primary"
type="button"
onClick={ () => setState({ isOpen: true }) }
>
Open about modal
</button>
<AboutContent
{...state}
onCancel={() => setState({ isOpen: false })}
/>
</React.Fragment>
)}
</State>
</Playground>
## API
<Props of={AboutContent} />
@@ -1,39 +0,0 @@
---
name: Viewport Download Form
menu: Components
route: /components/viewport-download-form
---
import { Playground, Props } from 'docz'
import { State } from 'react-powerplug'
import { ViewportDownloadForm } from './../index'
import NameSpace from '../../../__docs__/NameSpace'
# Viewport Download Form
## Basic usage
<Playground>
<State
initial={{}}
>
{({ state, setState }) => (
<React.Fragment>
<div>
<pre>{JSON.stringify(state, null, 2)}</pre>
</div>
<div style={{ maxWidth: '400px', margin: '0 auto' }}>
<ViewportDownloadForm />
</div>
</React.Fragment>
)}
</State>
</Playground>
## API
<Props of={ViewportDownloadForm} />
## Translation Namespace
<NameSpace name="ViewportDownloadForm" />
@@ -1,10 +0,0 @@
.LayoutExample {
background-color: var(--primary-background-color);
width: 160px;
height: 160px;
}
.GridLayout td {
padding: 5px;
border: 1px solid black;
}
@@ -1,49 +0,0 @@
---
name: Layout Button
menu: Components
route: /components/layout-button
---
import { Playground, Props } from 'docz'
import { State } from 'react-powerplug'
import { LayoutButton } from './../index.js'
import './layoutButton.css'
import NameSpace from '../../../__docs__/NameSpace'
# Layout Button
## Basic usage
<Playground>
<State initial={{
selectedCell: {
className: 'hover',
col: 1,
row: 1
},
}}>
{({ state, setState }) => (
<React.Fragment>
{/* STATE */}
<div><pre>{JSON.stringify(state, null, 2) }</pre></div>
{/* COMPONENT */}
<div className="LayoutExample">
<LayoutButton
{...state}
onChange={cell => setState({ selectedCell: cell })}
/>
</div>
</React.Fragment>
)}
</State>
</Playground>
## API
<Props of={LayoutButton} />
## Translation Namespace
<NameSpace name="Buttons" />
@@ -9,6 +9,14 @@ import { TableListItem } from './../tableList/TableListItem.js';
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 {
static propTypes = {
measurementData: PropTypes.object.isRequired,
@@ -21,6 +29,14 @@ class MeasurementTableItem extends Component {
t: PropTypes.func,
};
constructor(props) {
super(props);
this.state = {
collapsed: true,
visible: true,
};
}
render() {
const {
warningTitle = '',
@@ -93,27 +109,115 @@ class MeasurementTableItem extends Component {
actionButtons.push(deleteButton);
}
return (
<TableListItem
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: '|',
if (
this.props.measurementData.isSRText === true &&
this.props.measurementData.labels &&
this.props.measurementData.labels.length > 0
) {
return (
<React.Fragment>
<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>
<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 className="displayTexts">{this.getDataDisplayText()}</div>
{!this.props.measurementData.isReadOnly && (
<div className="rowActions">{actionButtons}</div>
)}
</div>
</TableListItem>
);
</TableListItem>
);
}
};
onItemClick = event => {
@@ -1,4 +1,16 @@
.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
margin-left: -1px;
background-color: var(--ui-gray-darker)
@@ -63,3 +75,16 @@
width: 22px;
height: 20px;
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;
@@ -1,49 +0,0 @@
---
name: Measurement Table
menu: Components
route: /components/measurement-table
---
import { Playground, Props } from 'docz'
import { State } from 'react-powerplug'
import { MeasurementTable } from './../index.js'
import NameSpace from '../../../__docs__/NameSpace'
//
import measurements from './measurements.js'
import timepoints from './timepoints.js'
import warnings from './warnings.js'
# Measurement Table
## Basic usage
<Playground>
<State
initial={{
// Spelling error: overwallWarnings
overwallWarnings: warnings,
timepoints,
measurementCollection: measurements,
}}
>
{({ state, setState }) => (
<div style={{ height: '340px' }}>
<MeasurementTable
{...state}
onItemClick={() => alert('onItemClick')}
onRelabelClick={() => alert('onRelabelClick')}
onDeleteClick={() => alert('onDeleteClick')}
onEditDescriptionClick={() => alert('onEditDescriptionClick')}
/>
</div>
)}
</State>
</Playground>
## API
<Props of={MeasurementTable} />
## Translation Namespace
<NameSpace name="MeasurementTable" />
@@ -1,93 +0,0 @@
export default [
{
maxMeasurements: 5,
groupName: 'Targets',
measurements: [
{
measurementId: '123',
measurementNumber: '123',
itemNumber: 1,
label: 'Chest Wall Posterior',
hasWarnings: true,
warningTitle: 'Criteria nonconformities',
isSplitLesion: false,
warningList: [
'All measurements should have a location',
'Nodal lesions must be >= 15mm short axis AND >= double the acquisition slice thickness by CT and MR',
],
data: [
{
displayText: '25.7 x 12.9',
},
{
displayText: '24.7 x 11.5',
},
{},
],
},
{
measurementId: '124',
measurementNumber: '124',
itemNumber: 2,
label: 'Bone Extremity',
data: [
{
displayText: '24.7 x 11.1',
},
{
displayText: '21.2 x 10.9',
},
{},
],
},
],
},
{
maxMeasurements: 3,
groupName: 'NonTargets',
measurements: [
{
measurementId: '125',
measurementNumber: '125',
itemNumber: 1,
label: 'Chest Wall Single Site',
data: [
{
displayText: 'MD',
},
{
displayText: 'NM',
},
{},
],
},
{
measurementId: '126',
measurementNumber: '126',
itemNumber: 2,
label: 'Extremity Multiple Sites',
data: [
{
displayText: 'CR',
},
{},
{},
],
},
{
measurementId: '127',
measurementNumber: '127',
label: 'Extremity Site',
data: [
{
displayText: 'CR',
},
{},
{
displayText: 'NM',
},
],
},
],
},
];
@@ -1,14 +0,0 @@
export default [
{
label: 'Follow-up 2',
date: '20-Dec-18',
},
{
label: 'Follow-up 1',
date: '15-Jun-18',
},
{
label: 'Baseline',
date: '10-Apr-18',
},
];
@@ -1,6 +0,0 @@
export default {
warningList: [
'All measurements should have a location',
'Nodal lesions must be >= 15mm short axis AND >= double the acquisition slice thickness by CT and MR',
],
};
@@ -1,39 +0,0 @@
---
name: OHIFModal
menu: Components
route: /components/ohif-modal
---
import { Playground, Props } from 'docz'
import { State } from 'react-powerplug'
import { OHIFModal } from './../index'
import NameSpace from '../../../__docs__/NameSpace'
# OHIF Modal
## Basic usage
<Playground>
<State
initial={{}}
>
{({ state, setState }) => (
<React.Fragment>
<div>
<pre>{JSON.stringify(state, null, 2)}</pre>
</div>
<div style={{ maxWidth: '400px', margin: '0 auto' }}>
<OHIFModal />
</div>
</React.Fragment>
)}
</State>
</Playground>
## API
<Props of={OHIFModal} />
## Translation Namespace
<NameSpace name="OHIFModal" />
@@ -1,84 +0,0 @@
---
name: Overlay Trigger
menu: Components
route: /components/overlay-trigger
---
import { Playground, Props } from 'docz'
import { Tooltip } from './../../tooltip'
import { OverlayTrigger } from './../index.js'
# Overlay Trigger
## Basic usage
### On Hover
<Playground>
<div style={{
display: 'flex',
justifyContent: 'space-between'
}}>
{
['right', 'left', 'bottom', 'top'].map((placement) => {
return (
<React.Fragment key={`example-${placement}`}>
<OverlayTrigger
key={`example-${placement}`}
placement={placement}
overlay={
<Tooltip
placement={placement}
className='in'
id={`tooltip-${placement}`}
>
Here I am!
</Tooltip>
}
>
<button> Tooltip on {placement}</button>
</OverlayTrigger>
</React.Fragment>
)
})
}
</div>
</Playground>
### On Click
<Playground>
<div
style={{
display: 'flex',
justifyContent: 'space-between',
}}
>
{['right', 'left', 'bottom', 'top'].map(placement => {
return (
<OverlayTrigger
key={`example-${placement}`}
placement={placement}
trigger="click"
overlay={
<Tooltip
placement={placement}
className="in"
id={`tooltip-${placement}`}
>
Here I am!
</Tooltip>
}
>
<button> Tooltip on {placement}</button>
</OverlayTrigger>
)
})}
</div>
</Playground>
## API
<Props of={OverlayTrigger} />
@@ -1,61 +0,0 @@
---
name: Quick Switch
menu: Components
route: /components/quick-switch
---
import { Playground, Props } from 'docz'
import { State } from 'react-powerplug'
import { QuickSwitch } from './../index.js'
//
import studies from './studies.js'
# Quick Switch
## Basic usage
<Playground>
<State initial={{
studyListData: studies,
}}>
{({ state, setState }) => (
<QuickSwitch
studyListData={state.studyListData}
// If this is always activeStudy's thumbnails,
// Why do we need to specify this?
seriesData={
state.studyListData.find((study) => {
return !!study.active;
}).thumbnails}
onSeriesSelected={(selectedSeries) => {
const { studyListData } = state;
const selectedId = selectedSeries.displaySetInstanceUID;
studyListData.forEach(study => {
study.thumbnails.forEach(series => {
series.active = series.displaySetInstanceUID === selectedId;
})
});
setState({ studyListData });
}}
onStudySelected={(selectedStudy) => {
const { studyListData } = state;
const selectedId = selectedStudy.StudyInstanceUID;
studyListData.forEach(study => {
study.active = study.StudyInstanceUID === selectedId;
});
setState({ studyListData });
}}
/>
)}
</State>
</Playground>
## API
<Props of={QuickSwitch} />
@@ -1,76 +0,0 @@
export default [
{
StudyInstanceUID: '10001',
StudyDescription: 'Anti-PD-1',
modalities: 'CT',
StudyDate: '18-nov-2018',
active: false,
thumbnails: [
{
imageSrc:
'https://raw.githubusercontent.com/crowds-cure/cancer/master/public/screenshots/Anti-PD-1_Lung.jpg',
SeriesDescription: 'Anti-PD-1_Lung',
SeriesNumber: '2',
numImageFrames: 512,
displaySetInstanceUID: '10001-1',
stackPercentComplete: 30,
},
{
imageSrc:
'https://raw.githubusercontent.com/crowds-cure/cancer/master/public/screenshots/Anti-PD-1_MELANOMA.jpg',
SeriesDescription: 'Anti-PD-1_MELANOMA',
SeriesNumber: '2',
InstanceNumber: '1',
numImageFrames: 256,
displaySetInstanceUID: '10001-2',
stackPercentComplete: 70,
},
],
},
{
StudyInstanceUID: '10002',
StudyDescription: 'CPTAC',
modalities: 'CT',
StudyDate: '16-aug-2017',
active: true,
thumbnails: [
{
imageSrc:
'https://raw.githubusercontent.com/crowds-cure/cancer/master/public/screenshots/CPTAC-GBM.jpg',
SeriesDescription: 'CPTAC-GBM',
active: true,
SeriesNumber: '2',
numImageFrames: 512,
displaySetInstanceUID: '10002-1',
stackPercentComplete: 100,
},
{
imageSrc:
'https://raw.githubusercontent.com/crowds-cure/cancer/master/public/screenshots/CPTAC-CM.jpg',
SeriesDescription: 'CPTAC-CM',
SeriesNumber: '2',
InstanceNumber: '1',
displaySetInstanceUID: '10002-2',
numImageFrames: 256,
},
{
imageSrc:
'https://raw.githubusercontent.com/crowds-cure/cancer/master/public/screenshots/CPTAC-HNSCC.jpg',
SeriesDescription: 'CPTAC-HNSCC',
SeriesNumber: '2',
InstanceNumber: '1',
displaySetInstanceUID: '10002-3',
numImageFrames: 256,
},
{
imageSrc:
'https://raw.githubusercontent.com/crowds-cure/cancer/master/public/screenshots/CPTAC-LSCC.jpg',
SeriesDescription: 'CPTAC-LSCC',
SeriesNumber: '2',
InstanceNumber: '1',
displaySetInstanceUID: '10002-4',
numImageFrames: 256,
},
],
},
];
@@ -1,74 +0,0 @@
---
name: Radio Button List
menu: Elements
route: /components/radio-button-list
---
import { Playground, Props } from 'docz'
import { State } from 'react-powerplug'
import { RadioButtonList } from './../index'
import NameSpace from '../../../__docs__/NameSpace'
#Radio Button List
## Basic usage
<Playground>
<State
initial={{
description : [
{
'label' : 'Button 1',
'id' : 'button1',
'checked' : false
},
{
'label' : 'Button 2',
'id' : 'button2',
'checked' : false,
},
{
'label' : 'Button 3',
'id' : 'button3',
'checked' : true,
}
]
}}
>
{({ state, setState }) => (
<React.Fragment>
<div>
<pre>{JSON.stringify(state, null, 2)}</pre>
</div>
<div style={{ maxWidth: '300px', margin: '0 auto' }}>
<RadioButtonList description={[
{
'label' : 'Button 1',
'id' : 'button1',
'checked' : false
},
{
'label' : 'Button 2',
'id' : 'button2',
'checked' : false,
},
{
'label' : 'Button 3',
'id' : 'coolButton3',
'checked' : true,
}
]}/>
</div>
</React.Fragment>
)}
</State>
</Playground>
## API
<Props of={RadioButtonList} />
## Translation Namespace
<NameSpace name="RadioButtonList" />
@@ -1,51 +0,0 @@
---
name: Rounded Button Group
menu: Components
route: /components/rounded-button-group
---
import { Playground, Props } from 'docz';
import { State } from 'react-powerplug';
import { RoundedButtonGroup } from './../index.js';
# Rounded Button Group
A basic styled toggle switch
## Basic usage
<Playground>
<State
initial={{
value: null,
options: [
{
value: 'studies',
icon: 'th',
bottomLabel: 'Series',
},
{
value: 'measurements',
icon: 'th-list',
bottomLabel: 'Measurements',
},
],
}}
>
{({ state, setState }) => (
<React.Fragment>
<div>
<pre>value: {JSON.stringify(state.value, null, 2)}</pre>
</div>
<RoundedButtonGroup
{...state}
onValueChanged={value => setState({ value })}
/>
</React.Fragment>
)}
</State>
</Playground>
## API
<Props of={RoundedButtonGroup} />
@@ -1,42 +0,0 @@
---
name: Select Tree
menu: Components
route: /components/select-tree
---
import { Playground, Props } from 'docz'
import { State } from 'react-powerplug'
import { SelectTree } from './../index.js'
import selectTreeItems from './selectTreeItems.js'
# SelectTree
## Basic usage
<Playground>
<State initial={{
items: selectTreeItems,
}}>
{({ state, setState }) => (
<React.Fragment>
<pre style={{ maxHeight: '135px', overflowX: 'auto' }}>
{JSON.stringify(state.items, null, 2)}
</pre>
<SelectTree
items={state.items}
onSelected={(event, item) => setState({ itemLabelSelected: item.label }) }
selectTreeFirstTitle="Select Tree Example Header"
autoFocus={false}
/>
</React.Fragment>
)}
</State>
</Playground>
## API
<Props of={SelectTree} />
@@ -1,30 +0,0 @@
export default [
{
label: 'A',
value: 'A',
items: [
{
label: 'Abdominal Wall',
value: 'AbdominalWall',
},
{
label: 'Adrenal Left',
value: 'AdrenalLeft',
},
],
},
{
label: 'B',
value: 'B',
items: [
{
label: 'Brain',
value: 'Brain',
},
{
label: 'Breast',
value: 'Breast',
},
],
},
];
Loaded 100 of 139 files, more files were not shown because too many files have changed in this diff. Show more