feat(tmtv-mode): Add Brush tools and move SUV peak calculation to web worker (#4053)
This commit is contained in:
1 parent
8c5ab942b9
commit
8192e348ec
27 files changed
+593
-212
No files matched your search
@@ -54,7 +54,7 @@
|
||||
"@cornerstonejs/codec-libjpeg-turbo-8bit": "^1.2.2",
|
||||
"@cornerstonejs/codec-openjpeg": "^1.2.2",
|
||||
"@cornerstonejs/codec-openjph": "^2.4.5",
|
||||
"@cornerstonejs/dicom-image-loader": "^1.70.9",
|
||||
"@cornerstonejs/dicom-image-loader": "^1.70.10",
|
||||
"@emotion/serialize": "^1.1.3",
|
||||
"@ohif/core": "3.8.0-beta.86",
|
||||
"@ohif/extension-cornerstone": "3.8.0-beta.86",
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
"@cornerstonejs/codec-libjpeg-turbo-8bit": "^1.2.2",
|
||||
"@cornerstonejs/codec-openjpeg": "^1.2.2",
|
||||
"@cornerstonejs/codec-openjph": "^2.4.2",
|
||||
"@cornerstonejs/dicom-image-loader": "^1.70.9",
|
||||
"@cornerstonejs/dicom-image-loader": "^1.70.10",
|
||||
"@ohif/ui": "3.8.0-beta.86",
|
||||
"cornerstone-math": "0.1.9",
|
||||
"dicom-parser": "^1.8.21"
|
||||
|
||||
@@ -107,8 +107,8 @@ export default class PanelService extends PubSubService {
|
||||
// stack the content of the panels in one react component
|
||||
content = () => (
|
||||
<>
|
||||
{panelsData.map(({ content: PanelContent }) => (
|
||||
<PanelContent key={entry.id} />
|
||||
{panelsData.map(({ content: PanelContent }, index) => (
|
||||
<PanelContent key={index} />
|
||||
))}
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -438,7 +438,11 @@ export default class ToolbarService extends PubSubService {
|
||||
}
|
||||
|
||||
if (Array.isArray(evaluate)) {
|
||||
const evaluators = evaluate.map(evaluatorName => {
|
||||
const evaluators = evaluate.map(evaluator => {
|
||||
const isObject = typeof evaluator === 'object';
|
||||
|
||||
const evaluatorName = isObject ? evaluator.name : evaluator;
|
||||
|
||||
const evaluateFunction = this._evaluateFunction[evaluatorName];
|
||||
|
||||
if (!evaluateFunction) {
|
||||
@@ -447,6 +451,10 @@ export default class ToolbarService extends PubSubService {
|
||||
);
|
||||
}
|
||||
|
||||
if (isObject) {
|
||||
return args => evaluateFunction({ ...args, ...evaluator });
|
||||
}
|
||||
|
||||
return evaluateFunction;
|
||||
});
|
||||
|
||||
|
||||
@@ -137,6 +137,36 @@ this pattern, where multiple toolbar buttons are using the same evaluator but wi
|
||||
},
|
||||
```
|
||||
|
||||
#### Composing evaluators
|
||||
|
||||
You can choose to set up multiple evaluators for a single button. This comes in handy when you need to assess the button according to various conditions. For example, we aim to prevent the Cine player from showing up on the 3D viewport, so we have:
|
||||
|
||||
```js
|
||||
evaluate: ['evaluate.cine', 'evaluate.not3D'],
|
||||
```
|
||||
|
||||
You can even come up with advanced evaluators such as:
|
||||
|
||||
```js
|
||||
evaluate: [
|
||||
'evaluate.cornerstone.segmentation',
|
||||
// need to put the disabled text last, since each evaluator will
|
||||
// merge the result text into the final result
|
||||
{
|
||||
name: 'evaluate.cornerstoneTool',
|
||||
disabledText: 'Select the PT Axial to enable this tool',
|
||||
},
|
||||
],
|
||||
```
|
||||
|
||||
that we use for our RectangleROIStartEndThreshold tool in tmtv mode.
|
||||
|
||||
As you see this evaluator is composed of two evaluators, one is `evaluate.cornerstone.segmentation` which makes sure (in the implementation), that
|
||||
there is a segmentation created, and the second one is `evaluate.cornerstoneTool` which makes sure that the tool is available in the viewport.
|
||||
|
||||
Since we are using multiple evaluators, the `disabledText` of each evaluator will be merged into the final result, so you need to
|
||||
put the `disabledText` in the last evaluator.
|
||||
|
||||
#### Group evaluators
|
||||
Split buttons (see in [ToolbarService](../../services/data/ToolbarService.md) on how to define one) may feature a group evaluator, we provide two of them and you can write your own.
|
||||
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="22px" height="22px" viewBox="0 0 22 22" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<title>tab-4d</title>
|
||||
<g id="tab-4d" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g id="Group" transform="translate(1, 1)" stroke="currentColor">
|
||||
<rect id="Rectangle" x="0" y="0" width="20" height="20" rx="2"></rect>
|
||||
<g id="Group-3" transform="translate(4, 5)" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M6.9072464,0.398231009 C9.3565092,0.398231009 11.342029,2.38375081 11.342029,4.83301362 C11.342029,7.28227642 9.3565092,9.26779623 6.9072464,9.26779623 L6.9072464,0.398231009 Z" id="Path"></path>
|
||||
<polyline id="Path-2" points="3.27497101 4.83301362 0 4.83301362 3.62401581 0 3.62401581 4.87475204 3.62401581 9.26779623"></polyline>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 970 B |
@@ -216,6 +216,9 @@ import layoutCommon2x2 from './../../assets/icons/layout-common-2x2.svg';
|
||||
import layoutCommon2x3 from './../../assets/icons/layout-common-2x3.svg';
|
||||
import iconToolRotate from './../../assets/icons/tool-3d-rotate.svg';
|
||||
|
||||
//
|
||||
import tab4D from './../../assets/icons/tab-4d.svg';
|
||||
|
||||
/** New investigational use */
|
||||
import investigationalUse from './../../assets/icons/illustration-investigational-use.svg';
|
||||
|
||||
@@ -426,6 +429,7 @@ const ICONS = {
|
||||
'layout-common-1x2': layoutCommon1x2,
|
||||
'layout-common-2x2': layoutCommon2x2,
|
||||
'layout-common-2x3': layoutCommon2x3,
|
||||
'tab-4d': tab4D,
|
||||
|
||||
/** New investigational use */
|
||||
'illustration-investigational-use': investigationalUse,
|
||||
|
||||
@@ -28,9 +28,12 @@ const SegmentItem = ({
|
||||
|
||||
return (
|
||||
<div
|
||||
className={classnames('text-aqua-pale group/row bg-primary-dark flex min-h-[28px] flex-col', {
|
||||
'bg-primary-light border-primary-light rounded-l-[6px] border text-black': isActive,
|
||||
})}
|
||||
className={classnames(
|
||||
'text-aqua-pale group/row bg-primary-dark flex min-h-[28px] flex-col overflow-hidden',
|
||||
{
|
||||
'bg-primary-light border-primary-light rounded-l-[6px] border text-black': isActive,
|
||||
}
|
||||
)}
|
||||
onClick={e => {
|
||||
e.stopPropagation();
|
||||
onClick(segmentationId, segmentIndex);
|
||||
@@ -151,11 +154,11 @@ const SegmentItem = ({
|
||||
</div>
|
||||
</div>
|
||||
{Array.isArray(displayText) ? (
|
||||
<div className="flex flex-col py-[5px] pl-[43px]">
|
||||
<div className="flex flex-col bg-black py-[5px] pl-[43px]">
|
||||
{displayText.map(text => (
|
||||
<div
|
||||
key={text}
|
||||
className="text-aqua-pale flex h-full items-center bg-black text-[11px]"
|
||||
className="text-aqua-pale flex h-full items-center text-[11px]"
|
||||
>
|
||||
{text}
|
||||
</div>
|
||||
@@ -163,7 +166,7 @@ const SegmentItem = ({
|
||||
</div>
|
||||
) : (
|
||||
displayText && (
|
||||
<div className="text-aqua-pale flex h-full items-center bg-black px-2 py-[5px] pl-[45px] text-[11px]">
|
||||
<div className="text-aqua-pale flex h-full items-center px-2 py-[5px] pl-[45px] text-[11px]">
|
||||
{displayText}
|
||||
</div>
|
||||
)
|
||||
|
||||
Reference in new issue
Block a user