Skip to content

Commit

Permalink
fix: Allow synchronizers to work with Stack Viewports (#192)
Browse files Browse the repository at this point in the history
  • Loading branch information
swederik committed Mar 21, 2022
1 parent 76d9099 commit 897573b
Show file tree
Hide file tree
Showing 12 changed files with 100 additions and 54 deletions.
3 changes: 3 additions & 0 deletions packages/cornerstone-render/src/RenderingEngine/Scene.ts
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,9 @@ class Scene {
*/
public getVolumeActors(): Array<ActorEntry> {
const viewports = this.getViewports()

// TODO: this is a bit confusing that this returns something different
// than getVolumeActor(). We should change getVolumeActor() I think
return viewports[0].getActors()
}
}
Expand Down
14 changes: 12 additions & 2 deletions packages/cornerstone-render/src/RenderingEngine/StackViewport.ts
Original file line number Diff line number Diff line change
Expand Up @@ -534,9 +534,19 @@ class StackViewport extends Viewport {

const { canvas, options } = this
const ctx = canvas.getContext("2d")
const rgb = options.background.map(f => Math.floor(255 * f));

ctx.fillStyle = `rgb(${rgb[0]}, ${rgb[1]}, ${rgb[2]})`
// Default to black if no background color is set
let fillStyle;
if (options && options.background) {
const rgb = options.background.map(f => Math.floor(255 * f));
fillStyle = `rgb(${rgb[0]}, ${rgb[1]}, ${rgb[2]})`
} else {
fillStyle = 'black'
}

// We draw over the previous stack with the background color while we
// wait for the next stack to load
ctx.fillStyle = fillStyle
ctx.fillRect(0, 0, canvas.width, canvas.height)

this._setImageIdIndex(currentImageIdIndex)
Expand Down
2 changes: 1 addition & 1 deletion packages/cornerstone-render/src/types/IViewportUID.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@
*/
export default interface IViewportUID {
renderingEngineUID: string
sceneUID: string
sceneUID?: string
viewportUID: string
}
Original file line number Diff line number Diff line change
Expand Up @@ -56,28 +56,28 @@ class Synchronizer {
this.addSource(viewport)
}

public addSource(viewport: Types.IViewportUID) {
const { renderingEngineUID, sceneUID, viewportUID } = viewport
public addSource(viewport: Types.IViewportUID): void {
if (this._sourceViewports.includes(viewport)) {
return
}
const { renderingEngineUID, viewportUID } = viewport

// TODO: exit early if already in list
const canvas = getRenderingEngine(renderingEngineUID)
.getScene(sceneUID)
.getViewport(viewportUID)
.getCanvas()

// const enabledElement = getEnabledElement(canvas)

// @ts-ignore
canvas.addEventListener(this._eventName, this._onEvent.bind(this))
this._updateDisableHandlers()

this._sourceViewports.push(viewport)
}

public addTarget(viewport: Types.IViewportUID) {
// const { renderingEngineUID, sceneUID, viewportUID } = viewport
public addTarget(viewport: Types.IViewportUID): void {
if (this._targetViewports.includes(viewport)) {
return
}

// TODO: exit early if already in list
this._targetViewports.push(viewport)
this._updateDisableHandlers()
}
Expand All @@ -92,12 +92,12 @@ class Synchronizer {
this._targetViewports.forEach((t) => this.removeTarget(t))
}

public remove(viewport: Types.IViewportUID) {
public remove(viewport: Types.IViewportUID): void {
this.removeTarget(viewport)
this.removeSource(viewport)
}

public removeSource(viewport: Types.IViewportUID) {
public removeSource(viewport: Types.IViewportUID): void {
const index = _getViewportIndex(this._sourceViewports, viewport)

if (index === -1) {
Expand All @@ -112,8 +112,8 @@ class Synchronizer {
this._updateDisableHandlers()
}

public removeTarget(viewport: Types.IViewportUID) {
const index = _getViewportIndex(this._sourceViewports, viewport)
public removeTarget(viewport: Types.IViewportUID): void {
const index = _getViewportIndex(this._targetViewports, viewport)

if (index === -1) {
return
Expand All @@ -123,16 +123,12 @@ class Synchronizer {
this._updateDisableHandlers()
}

public hasSourceViewport(renderingEngineUID, sceneUID, viewportUID) {
// Exact match; could make loose
const containsExactMatch = this._sourceViewports.some(
public hasSourceViewport(renderingEngineUID: string, viewportUID: string): boolean {
return this._sourceViewports.some(
(vp) =>
vp.renderingEngineUID === renderingEngineUID &&
vp.sceneUID === sceneUID &&
vp.viewportUID === viewportUID
)

return containsExactMatch
}

public fireEvent(sourceViewport: Types.IViewportUID, sourceEvent: any): void {
Expand Down Expand Up @@ -160,7 +156,7 @@ class Synchronizer {
}
}

private _onEvent(evt: any) {
private _onEvent(evt: any): void {
if (this._ignoreFiredEvents === true) {
return
}
Expand Down Expand Up @@ -195,7 +191,6 @@ class Synchronizer {

viewports.forEach(function (vUid) {
const canvas = getRenderingEngine(vUid.renderingEngineUID)
.getScene(vUid.sceneUID)
.getViewport(vUid.viewportUID)
.getCanvas()

Expand All @@ -208,7 +203,10 @@ class Synchronizer {
}
}

function _getUniqueViewports(vp1, vp2) {
function _getUniqueViewports(
vp1: Array<Types.IViewportUID>,
vp2: Array<Types.IViewportUID>
): Array<Types.IViewportUID> {
const unique = []

const vps = vp1.concat(vp2)
Expand All @@ -219,7 +217,6 @@ function _getUniqueViewports(vp1, vp2) {
!unique.some(
(u) =>
vp.renderingEngineUID === u.renderingEngineUID &&
vp.sceneUID === u.sceneUID &&
vp.viewportUID === u.viewportUID
)
) {
Expand All @@ -230,20 +227,24 @@ function _getUniqueViewports(vp1, vp2) {
return unique
}

function _getViewportIndex(arr, vp) {
function _getViewportIndex(
arr: Array<Types.IViewportUID>,
vp: Types.IViewportUID
): number {
return arr.findIndex(
(ar) =>
vp.renderingEngineUID === ar.renderingEngineUID &&
vp.sceneUID === ar.sceneUID &&
vp.viewportUID === ar.viewportUID
)
}

function _getViewportCanvas(vp: Types.IViewportUID) {
return getRenderingEngine(vp.renderingEngineUID)
.getScene(vp.sceneUID)
.getViewport(vp.viewportUID)
.getCanvas()
function _getViewportCanvas(vp: Types.IViewportUID): HTMLCanvasElement {
const renderingEngine = getRenderingEngine(vp.renderingEngineUID);
if (!renderingEngine) {
throw new Error(`No RenderingEngine for UID: ${vp.renderingEngineUID}`)
}

return renderingEngine.getViewport(vp.viewportUID).getCanvas()
}

export default Synchronizer
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,13 @@ function createSynchronizer(
synchronizerId: string,
eventName: string,
eventHandler: ISynchronizerEventHandler
): Synchronizer | undefined {
): Synchronizer {
const toolGroupWithIdExists = state.synchronizers.some(
(tg) => tg.id === synchronizerId
)

if (toolGroupWithIdExists) {
console.warn(`'${synchronizerId}' already exists.`)
return
throw new Error(`Synchronizer with id '${synchronizerId}' already exists.`)
}

// Create
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ function getSynchronizers({
const notDisabled = !synchronizer.isDisabled()
const hasSourceViewport = synchronizer.hasSourceViewport(
renderingEngineUID,
sceneUID,
viewportUID
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ import getToolsWithDataForElement from './getToolsWithDataForElement'
* short circuit early
*
* @param element canvas element
* @returns {string} toolDataUID that is cancelled
* @returns {string | undefined} toolDataUID that is cancelled
*/
export default function cancelActiveManipulations(element: HTMLElement): void {
export default function cancelActiveManipulations(element: HTMLElement): string | undefined {
const tools = getToolsWithModesForElement(element, [
ToolModes.Active,
ToolModes.Passive,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,14 @@ export default function cameraSyncCallback(

const { camera } = cameraModifiedEvent.detail

const tViewport = getRenderingEngine(targetViewport.renderingEngineUID)
.getScene(targetViewport.sceneUID)
.getViewport(targetViewport.viewportUID)
const renderingEngine = getRenderingEngine(targetViewport.renderingEngineUID)
if (!renderingEngine) {
throw new Error(`No RenderingEngine for UID: ${targetViewport.renderingEngineUID}`)
}

const tViewport = renderingEngine.getViewport(targetViewport.viewportUID)

// TODO: only sync in-plane movements if one viewport is a stack viewport

tViewport.setCamera(camera)
tViewport.render()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { getRenderingEngine, Types } from '@ohif/cornerstone-render'
import { getRenderingEngine, StackViewport, Types, VolumeViewport } from '@ohif/cornerstone-render'

/**
* @function cameraSyncCallback - Synchronizer callback to synchronize the voi of volumeActors of identical volumes
Expand Down Expand Up @@ -27,18 +27,36 @@ export default function voiSyncCallback(

const tScene = renderingEngine.getScene(targetViewport.sceneUID)

if (tScene.uid === sceneUID) {
if (tScene && tScene.uid === sceneUID) {
// Same scene, no need to update.
return
}

const tViewport = tScene.getViewport(targetViewport.viewportUID)
const volumeActor = tScene.getVolumeActor(volumeUID)
const tViewport = renderingEngine.getViewport(targetViewport.viewportUID)

volumeActor
.getProperty()
.getRGBTransferFunction(0)
.setRange(range.lower, range.upper)
if (tViewport instanceof VolumeViewport) {
const scene = renderingEngine.getScene(tViewport.sceneUID)
let volumeActor = scene.getVolumeActor(volumeUID)

// TODO: This may not be what we want. It is a fallback
// for cases when we are syncing from a stack to a volume viewport
if (!volumeActor) {
// TODO: this is a bit confusing that this returns something different
// than getVolumeActor(). We should change getVolumeActor() I think
volumeActor = scene.getVolumeActors()[0].volumeActor
}

volumeActor
.getProperty()
.getRGBTransferFunction(0)
.setRange(range.lower, range.upper)
} else if (tViewport instanceof StackViewport) {
tViewport.setProperties({
voi: range
})
} else {
throw new Error('Viewport type not supported.')
}

tViewport.render()
}
15 changes: 12 additions & 3 deletions packages/demo/src/ExampleFlipViewport.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ class FlipViewportExample extends Component {
this.axialSync = createCameraPositionSynchronizer('axialSync')
// this.sagittalSync = createCameraPositionSynchronizer('sagittalSync')
// this.coronalSync = createCameraPositionSynchronizer('coronalSync')
// this.ctWLSync = createVOISynchronizer('ctWLSync')
this.ctWLSync = createVOISynchronizer('ctWLSync')
// this.ptThresholdSync = createVOISynchronizer('ptThresholdSync')

this.viewportGridResizeObserver = new ResizeObserver((entries) => {
Expand Down Expand Up @@ -206,6 +206,15 @@ class FlipViewportExample extends Component {
VIEWPORT_IDS.STACK.CT
)


this.axialSync.add({ renderingEngineUID, viewportUID: VIEWPORT_IDS.CT.AXIAL });
this.axialSync.add({ renderingEngineUID, viewportUID: VIEWPORT_IDS.STACK.CT });

this.ctWLSync.add({ renderingEngineUID, viewportUID: VIEWPORT_IDS.CT.AXIAL });
this.ctWLSync.add({ renderingEngineUID, viewportUID: VIEWPORT_IDS.CT.CORONAL });
this.ctWLSync.add({ renderingEngineUID, viewportUID: VIEWPORT_IDS.CT.SAGITTAL });
this.ctWLSync.add({ renderingEngineUID, viewportUID: VIEWPORT_IDS.STACK.CT });

renderingEngine.render()

const ctStackViewport = renderingEngine.getViewport(VIEWPORT_IDS.STACK.CT)
Expand Down Expand Up @@ -278,7 +287,7 @@ class FlipViewportExample extends Component {
}

showOffScreenCanvas = () => {
// remove all children
// remove children
this._offScreenRef.current.innerHTML = ''
const uri = this.renderingEngine._debugRender()
const image = document.createElement('img')
Expand All @@ -289,7 +298,7 @@ class FlipViewportExample extends Component {
}

hidOffScreenCanvas = () => {
// remove all children
// remove children
this._offScreenRef.current.innerHTML = ''
}

Expand Down
2 changes: 1 addition & 1 deletion packages/demo/src/ExampleStackViewport.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -400,7 +400,7 @@ class StackViewportExample extends Component {
const element = evt.currentTarget
if (evt.code === 'Escape') {
const toolDataUID = cancelActiveManipulations(element)
if (toolDataUID) {
if (!!toolDataUID) {
this.setState({ cancelledMeasurements: toolDataUID })

if (this.state.deleteOnToolCancel) {
Expand Down
2 changes: 2 additions & 0 deletions packages/demo/src/constants/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,8 @@ const TOOL_GROUP_UIDS = {

// A string helper for the drop down.
const PET_CT_ANNOTATION_TOOLS = [
'Pan',
'Zoom',
'Probe',
'Crosshairs',
'Length',
Expand Down

0 comments on commit 897573b

Please sign in to comment.