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:
Alireza 2021-12-20 14:34:51 -05:00 committed by GitHub
parent 1d0b310d92
commit 65563edb54
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 394 additions and 7 deletions

View File

@ -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`,
},

View File

@ -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}

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 200 KiB

View 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.
![Template Extension](../../assets/img/template-extension-files.png)
### 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! 🎉

View 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.
![Template Mode](../../assets/img/template-mode-files.png)
### 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! 🎉
![](../../assets/img/template-mode-ui.png)

View File

@ -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",

View File

@ -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;
} */