feat: Cornerstone extension viewport default enabledElement handler and installation guides (#2636)
* feat: Add initial doc for mode installation * fix: cornerstone viewport should have default enabledElement handler * docs: Add custom mode and extension installation guides * docs: Add route address for template mode
This commit is contained in:
parent
1d0b310d92
commit
65563edb54
@ -34,7 +34,7 @@ module.exports = (env, argv, { SRC_DIR, DIST_DIR }) => {
|
||||
|
||||
const config = {
|
||||
mode: isProdBuild ? 'production' : 'development',
|
||||
devtool: isProdBuild ? 'source-map' : 'eval',
|
||||
devtool: isProdBuild ? 'source-map' : 'eval-source-map',
|
||||
entry: {
|
||||
app: `${SRC_DIR}/index.js`,
|
||||
},
|
||||
|
||||
@ -4,14 +4,21 @@ import cornerstone from 'cornerstone-core';
|
||||
import cornerstoneTools from 'cornerstone-tools';
|
||||
import CornerstoneViewport from 'react-cornerstone-viewport';
|
||||
import OHIF from '@ohif/core';
|
||||
import ViewportLoadingIndicator from './ViewportLoadingIndicator';
|
||||
import setCornerstoneMeasurementActive from './_shared/setCornerstoneMeasurementActive';
|
||||
import ViewportOverlay from './ViewportOverlay';
|
||||
|
||||
import { useCine, useViewportGrid } from '@ohif/ui';
|
||||
|
||||
import ViewportLoadingIndicator from './ViewportLoadingIndicator';
|
||||
import setCornerstoneMeasurementActive from './_shared/setCornerstoneMeasurementActive';
|
||||
import setActiveAndPassiveToolsForElement from './_shared/setActiveAndPassiveToolsForElement';
|
||||
import getTools from './_shared/getTools';
|
||||
|
||||
import ViewportOverlay from './ViewportOverlay';
|
||||
|
||||
const scrollToIndex = cornerstoneTools.importInternal('util/scrollToIndex');
|
||||
|
||||
const BaseAnnotationTool = cornerstoneTools.importInternal(
|
||||
'base/BaseAnnotationTool'
|
||||
);
|
||||
|
||||
const { StackManager } = OHIF.utils;
|
||||
|
||||
function OHIFCornerstoneViewport({
|
||||
@ -45,6 +52,42 @@ function OHIFCornerstoneViewport({
|
||||
element.addEventListener(cornerstone.EVENTS.IMAGE_RENDERED, handler);
|
||||
};
|
||||
|
||||
const defaultOnElementEnabled = evt => {
|
||||
const eventData = evt.detail;
|
||||
const targetElement = eventData.element;
|
||||
const tools = getTools();
|
||||
const toolAlias = ToolBarService.state.primaryToolId;
|
||||
|
||||
// Activate appropriate tool bindings for element
|
||||
setActiveAndPassiveToolsForElement(targetElement, tools);
|
||||
cornerstoneTools.setToolActiveForElement(targetElement, toolAlias, {
|
||||
mouseButtonMask: 1,
|
||||
});
|
||||
|
||||
// Set dashed, based on tracking, for this viewport
|
||||
const allTools = cornerstoneTools.store.state.tools;
|
||||
const toolsForElement = allTools.filter(
|
||||
tool => tool.element === targetElement
|
||||
);
|
||||
|
||||
toolsForElement.forEach(tool => {
|
||||
if (tool instanceof BaseAnnotationTool) {
|
||||
const configuration = tool.configuration;
|
||||
|
||||
configuration.renderDashed = true;
|
||||
|
||||
tool.configuration = configuration;
|
||||
}
|
||||
});
|
||||
|
||||
// Update image after setting tool config
|
||||
const enabledElement = cornerstone.getEnabledElement(targetElement);
|
||||
|
||||
if (enabledElement.image) {
|
||||
cornerstone.updateImage(targetElement);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
isMounted.current = true;
|
||||
return () => {
|
||||
@ -174,7 +217,9 @@ function OHIFCornerstoneViewport({
|
||||
return (
|
||||
<div className="relative flex flex-row w-full h-full overflow-hidden">
|
||||
<CornerstoneViewport
|
||||
onElementEnabled={onElementEnabled}
|
||||
onElementEnabled={
|
||||
onElementEnabled ? onElementEnabled : defaultOnElementEnabled
|
||||
}
|
||||
viewportIndex={viewportIndex}
|
||||
imageIds={imageIds}
|
||||
imageIdIndex={initialImageIdIndex}
|
||||
|
||||
BIN
platform/docs/docs/assets/img/template-extension-files.png
Normal file
BIN
platform/docs/docs/assets/img/template-extension-files.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 21 KiB |
BIN
platform/docs/docs/assets/img/template-mode-files.png
Normal file
BIN
platform/docs/docs/assets/img/template-mode-files.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 18 KiB |
BIN
platform/docs/docs/assets/img/template-mode-ui.png
Normal file
BIN
platform/docs/docs/assets/img/template-mode-ui.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 200 KiB |
121
platform/docs/docs/platform/extensions/installation.md
Normal file
121
platform/docs/docs/platform/extensions/installation.md
Normal file
@ -0,0 +1,121 @@
|
||||
---
|
||||
sidebar_position: 5
|
||||
sidebar_label: Installation
|
||||
---
|
||||
|
||||
# Extension: Installation
|
||||
|
||||
OHIF-v3 provides the ability to utilize external extensions. In this document we
|
||||
will describe how to add/install external extensions.
|
||||
|
||||
> Our long-term plan is to make OHIF-v3 capable of installing extensions from
|
||||
> `npm` with a command line. Until then, please use the instructions below to
|
||||
> manually install extensions.
|
||||
|
||||
## Installing an Extension
|
||||
|
||||
### 1) Extension Files Copy
|
||||
|
||||
We use a [Template Extension](https://github.com/OHIF/extension-template)
|
||||
repository to describe the necessary steps to use a new extension. You can use
|
||||
this repository as a starting point to create your own extension.
|
||||
|
||||
As you can see in the extension code base, folders structure are similar to the
|
||||
OHIF-maintained extensions. Let's look at our `Template Extension`:
|
||||
|
||||
- `src/index.js`: The most important file in any extension. This file is where
|
||||
extensions' authors hav defined the extension modules, lifecycle hooks, and
|
||||
other configurations.
|
||||
|
||||
For instance the `Template Extension` has the following modules which will be
|
||||
registered in OHIF by [Extension Manager](./extension.md)
|
||||
|
||||
Each extension has an ID which is used to register the extension in OHIF. For
|
||||
instance, for the `Template Extension`, the extension ID is
|
||||
`extension.template`.
|
||||
|
||||
```js {2} title="templateExtension/src/index.js"
|
||||
export default {
|
||||
id: 'extension.template',
|
||||
getPanelModule,
|
||||
getCommandsModule,
|
||||
};
|
||||
```
|
||||
|
||||
#### Package.json
|
||||
|
||||
Extension package name is defined in the `package.json` file. The `package.json`
|
||||
file is a JSON file that defines the extension name, version, and dependencies.
|
||||
For instance for the Template extension, the `package.json` file looks like
|
||||
this:
|
||||
|
||||
```js {2} title="templateExtension/package.json"
|
||||
{
|
||||
"name": "@ohif/extension-template",
|
||||
"version": "1.0.0",
|
||||
"description": "A template extension to show extension installation",
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
Note 1: We will use the `@ohif/extension-template` inside OHIF to let OHIF know
|
||||
about existence of this extension.
|
||||
|
||||
Note 2: You don't need to use the `@ohif` scope for your extensions. You can use
|
||||
any scope you want.
|
||||
|
||||
Note 3: Although folders names are not important and the `package.json` file
|
||||
contains the mode name, we recommend to use the same name as the folder name.
|
||||
|
||||

|
||||
|
||||
### 2) Configuring OHIF
|
||||
|
||||
There are couple of places inside OHIF which we need to modify in order to add
|
||||
the extension. The following lines of code should be added to the OHIF:
|
||||
|
||||
#### Viewer's package.json
|
||||
|
||||
```js {8} titl="platform/viewer/package.json"
|
||||
/* ... */
|
||||
"dependencies": {
|
||||
/* ... */
|
||||
"@babel/runtime": "7.7.6",
|
||||
"@ohif/core": "^2.5.1",
|
||||
"@ohif/extension-cornerstone": "^2.4.0",
|
||||
"@ohif/extension-measurement-tracking": "^0.0.1",
|
||||
"@ohif/extension-template": "^0.0.1",
|
||||
/* ... */
|
||||
}
|
||||
```
|
||||
|
||||
#### index.js
|
||||
|
||||
```js {4,13} title="platform/viewer/src/index.js"
|
||||
/* ... */
|
||||
import OHIFMeasurementTrackingExtension from '@ohif/extension-measurement-tracking';
|
||||
import OHIFDICOMSRExtension from '@ohif/extension-dicom-sr';
|
||||
import OHIFTemplateExtension from '@ohif/extension-template';
|
||||
|
||||
const appProps = {
|
||||
config: window ? window.config : {},
|
||||
defaultExtensions: [
|
||||
OHIFDefaultExtension,
|
||||
OHIFCornerstoneExtension,
|
||||
OHIFMeasurementTrackingExtension,
|
||||
OHIFDICOMSRExtension,
|
||||
OHIFTemplateExtension,
|
||||
],
|
||||
};
|
||||
/* ... */
|
||||
```
|
||||
|
||||
After you followed the above steps, you should run `yarn install` in the root
|
||||
folder of the OHIF repository to install the registered extension.
|
||||
|
||||
Now you have added the extension to the OHIF, and its modules (layout, commands,
|
||||
panels, toolbars, hangingProtocols, etc.) are made available to the OHIF
|
||||
`modes`. Read more on how to consume extensions
|
||||
[here](../modes/index.md#consuming-extensions)
|
||||
|
||||
Congrats! 🎉
|
||||
208
platform/docs/docs/platform/modes/installation.md
Normal file
208
platform/docs/docs/platform/modes/installation.md
Normal file
@ -0,0 +1,208 @@
|
||||
---
|
||||
sidebar_position: 5
|
||||
sidebar_label: Installation
|
||||
---
|
||||
|
||||
# Mode: Installation
|
||||
|
||||
OHIF-v3 provides the ability to utilize external modes and extensions. In this
|
||||
document we will describe how to add/install external modes.
|
||||
|
||||
> Our long-term plan is to make OHIF-v3 capable of installing modes at runtime,
|
||||
> however in the meantime, please use the instructions below to manually install
|
||||
> modes and their extensions.
|
||||
|
||||
## Installing a Mode
|
||||
|
||||
### 1) Mode Files Copy
|
||||
|
||||
We use a [Template Mode](https://github.com/OHIF/mode-template) repository to
|
||||
demonstrate how to install an external mode. This repository also includes all
|
||||
the files required to create a new mode. You can use this repository as a
|
||||
starting point to create your own mode.
|
||||
|
||||
As you can see in the Template mode
|
||||
[code base](https://github.com/OHIF/mode-template), folders structure are
|
||||
similar to the OHIF-maintained modes. Let's have more detailed look at the
|
||||
structure of the `Template mode`:
|
||||
|
||||
- `src/index.js`: The most important file in any mode. This file is where modes'
|
||||
authors hav defined the mode configurations such as:
|
||||
- The layout and the panels for left and right side.
|
||||
- LifeCycle hooks such as `onModeEnter` and `onModeExit`
|
||||
- Other files/folders/configs: .webpack, LICENSE, README.md, babel.config.js
|
||||
|
||||
Note: It is highly recommended to use the `Template Mode` as a starting point
|
||||
for your own mode. This way, you can easily reuse the necessary files and
|
||||
folders.
|
||||
|
||||
#### Package.json
|
||||
|
||||
Mode name is defined in the `package.json` file. The `package.json` file is a
|
||||
JSON file that defines the mode name, version, and dependencies. For instance
|
||||
for the Template mode, the `package.json` file looks like this:
|
||||
|
||||
```js {2} title="templateMode/package.json"
|
||||
{
|
||||
"name": "@ohif/mode-template",
|
||||
"version": "0.0.1",
|
||||
"description": "A template mode for installation demonstration",
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
Note 1: We will use the `@ohif/mode-template` inside OHIF to let OHIF know about
|
||||
existence of this mode.
|
||||
|
||||
Note 2: You don't need to use the `@ohif` scope for your modes/extensions. You
|
||||
can use any scope you want or none at all.
|
||||
|
||||
Note 3: Although folders names are not important and the `package.json` file
|
||||
contains the mode name, we recommend to use the same name for the folder name.
|
||||
|
||||

|
||||
|
||||
### 2) Configuring OHIF
|
||||
|
||||
In order to install/register the mode, we must make changes to a few areas
|
||||
inside OHIF. The OHIF should be updated using the following lines of code:
|
||||
|
||||
#### Viewer's package.json
|
||||
|
||||
```js {6} titl="platform/viewer/package.json"
|
||||
/* ... */
|
||||
"dependencies": {
|
||||
/* ... */
|
||||
"@ohif/i18n": "^0.52.8",
|
||||
"@ohif/mode-longitudinal": "^0.0.1",
|
||||
"@ohif/mode-template": "^0.0.1",
|
||||
"@ohif/ui": "^2.0.0",
|
||||
"@types/react": "^16.0.0",
|
||||
/* ... */
|
||||
}
|
||||
```
|
||||
|
||||
#### App.jsx
|
||||
|
||||
```js {3} title="platform/viewer/src/App.jsx"
|
||||
/* ... */
|
||||
import '@ohif/mode-longitudinal';
|
||||
import '@ohif/mode-template';
|
||||
/* ... */
|
||||
```
|
||||
|
||||
#### appInit.js
|
||||
|
||||
```js {4} title="platform/viewer/src/appInit.js"
|
||||
/* ... */
|
||||
if (!appConfig.modes.length) {
|
||||
appConfig.modes.push(window.longitudinalMode);
|
||||
appConfig.modes.push(window.templateMode);
|
||||
}
|
||||
/* ... */
|
||||
```
|
||||
|
||||
Note that we are assigning mode configuration objects from the `window` object;
|
||||
therefore, we should use the reference to the name of the mode which were
|
||||
defined in the last line of `src/index.js` file in mode configuration
|
||||
|
||||
```js {8} title="templateMode/src/index.js"
|
||||
/* ... */
|
||||
export default function mode({ modeConfiguration }) {
|
||||
return {
|
||||
/** */
|
||||
};
|
||||
}
|
||||
|
||||
window.templateMode = mode({});
|
||||
```
|
||||
|
||||
### Required Extensions for a Mode
|
||||
|
||||
Some modes require external extensions to be installed. For instance, the
|
||||
`@ohif/mode-longitudinal` mode requires the `@ohif/cornerstone` extension to be
|
||||
registered/installed in OHIF which is available in the OHIF-v3 repository.
|
||||
|
||||
How do we know that a mode requires an extension? (Until we have a more proper
|
||||
dependency management for modes and extensions) you can take a look inside the
|
||||
mode itself. Mode is a configuration file that defines the layout
|
||||
(layoutModule), panels (panelModule), viewport (viewportModule), and tools
|
||||
(commands) that are used to create an application at a given route. By looking
|
||||
inside the mode configuration file (`index.js`), you can see which extensions
|
||||
are required by the mode in the `extensions` property.
|
||||
|
||||
```js {12-16} title="platform/viewer/src/appInit.js"
|
||||
export default function mode({ modeConfiguration }) {
|
||||
return {
|
||||
id: 'template',
|
||||
displayName: 'Template Mode',
|
||||
/** ... */
|
||||
|
||||
routes: [
|
||||
{
|
||||
/** ... */
|
||||
},
|
||||
],
|
||||
extensions: [
|
||||
'extension.template',
|
||||
'org.ohif.default',
|
||||
'org.ohif.cornerstone',
|
||||
],
|
||||
/** ... */
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
As seen, our `Template Mode` requires the `org.ohif.default`,
|
||||
`org.ohif.cornerstone` and `extension.template` extensions.
|
||||
|
||||
> Note: Currently extensions dependencies are not handled by OHIF from the
|
||||
> `extensions` property. We will be adding this feature in the future.
|
||||
|
||||
In addition to the `extensions` property, the `mode` configuration object also
|
||||
has the reference for each module that is used. For instance, the `index.js`
|
||||
file in the `@ohif/mode-template` mode looks like this:
|
||||
|
||||
```js {10} title="clockMode/src/index.js"
|
||||
// ....
|
||||
routes: [
|
||||
{
|
||||
path: "template",
|
||||
layoutTemplate: ({ location, servicesManager }) => {
|
||||
return {
|
||||
id: ohif.layout,
|
||||
props: {
|
||||
leftPanels: [],
|
||||
rightPanels: ["extension.template.panelModule.clockPanel"],
|
||||
viewports: [
|
||||
{
|
||||
namespace: "org.ohif.cornerstone.viewportModule.cornerstone",
|
||||
displaySetsToDisplay: ["org.ohif.default.sopClassHandlerModule.stack"],
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
},
|
||||
},
|
||||
],
|
||||
// ....
|
||||
```
|
||||
|
||||
As seen, the right panel is defined as
|
||||
`"extension.template.panelModule.clockPanel"` which means that the
|
||||
`@ohif/mode-template` mode requires the `extension.template`. You can read more
|
||||
about installing extensions in the
|
||||
[Extension Installation](../extensions/installation.md)
|
||||
|
||||
> Additionally you can check the commands that the toolbar buttons will execute
|
||||
> in the `toolbarButtons` and see if any of them requires an extension.
|
||||
|
||||
After you installed the extension, you need to run `yarn install` in the root
|
||||
folder of the OHIF repository to install the registered extension and modes.
|
||||
|
||||
Running `yarn dev` will then start the application with the installed mode, by
|
||||
navigating to the `/template` route (e.g.,
|
||||
http://localhost:3000/template?StudyInstanceUIDs=1.3.6.1.4.1.14519.5.2.1.2744.7002.150059977302243314164020079415)
|
||||
you can see the clock panel. Congrats! 🎉
|
||||
|
||||

|
||||
@ -15,6 +15,7 @@
|
||||
"scripts": {
|
||||
"docusaurus": "docusaurus",
|
||||
"start": "docusaurus start --port 8001",
|
||||
"dev": "docusaurus start --port 8001",
|
||||
"docs": "docusaurus start --port 8001",
|
||||
"build": "docusaurus build",
|
||||
"swizzle": "docusaurus swizzle",
|
||||
|
||||
@ -207,7 +207,6 @@ article header h3 {
|
||||
--ifm-button-background-color: #e8f7f7;
|
||||
}
|
||||
|
||||
|
||||
.admonition-icon svg {
|
||||
fill: #0151d9;
|
||||
}
|
||||
@ -225,6 +224,19 @@ article header h3 {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.docusaurus-highlight-code-line {
|
||||
background-color: rgb(206, 208, 211);
|
||||
display: block;
|
||||
margin: 0 calc(-1 * var(--ifm-pre-padding));
|
||||
padding: 0 var(--ifm-pre-padding);
|
||||
}
|
||||
|
||||
/* If you have a different syntax highlighting theme for dark mode. */
|
||||
html[data-theme='dark'] .docusaurus-highlight-code-line {
|
||||
/* Color which works with dark mode syntax highlighting theme */
|
||||
background-color: rgb(100, 100, 100);
|
||||
}
|
||||
|
||||
/* .DocSearch {
|
||||
display: none;
|
||||
} */
|
||||
|
||||
Loading…
Reference in New Issue
Block a user