Skip to content

Commit

Permalink
fix: Crosshair and panTool for flipped viewport (#159)
Browse files Browse the repository at this point in the history
* fix: crosshairs for non-synced viewports and fixed pan and tool for flip

* fix: crosshairs for flipped viewport

* fix: padding for probe
  • Loading branch information
sedghi authored Jun 30, 2021
1 parent be8d82d commit 35152ea
Show file tree
Hide file tree
Showing 9 changed files with 141 additions and 88 deletions.
79 changes: 60 additions & 19 deletions packages/cornerstone-render/src/RenderingEngine/Viewport.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
import { vtkCamera } from 'vtk.js/Sources/Rendering/Core/Camera'
import vtkVolume from 'vtk.js/Sources/Rendering/Core/Volume'
import vtkMatrixBuilder from 'vtk.js/Sources/Common/Core/MatrixBuilder'
import { vec3, mat4 } from 'gl-matrix'
import _cloneDeep from 'lodash.clonedeep'

import Events from '../enums/events'
import VIEWPORT_TYPE from '../constants/viewportType'
import { ICamera, ViewportInput, ActorEntry } from '../types'
import _cloneDeep from 'lodash.clonedeep'
import renderingEngineCache from './renderingEngineCache'
import RenderingEngine from './RenderingEngine'
import triggerEvent from '../utilities/triggerEvent'
import { triggerEvent, isEqual } from '../utilities'
import vtkMath from 'vtk.js/Sources/Common/Core/Math'
import vtkVolume from 'vtk.js/Sources/Rendering/Core/Volume'
import { vec3 } from 'gl-matrix'
import vtkMatrixBuilder from 'vtk.js/Sources/Common/Core/MatrixBuilder'
import { ViewportInputOptions, Point2, Point3 } from '../types'
import { vtkSlabCamera } from './vtkClasses'
import ORIENTATION from '../constants/orientation'

/**
* An object representing a single viewport, which is a camera
Expand Down Expand Up @@ -206,7 +208,14 @@ class Viewport {
public applyFlipTx = (worldPos: Point3): Point3 => {
// One vol actor is enough to get the flip direction. If not flipped
// the transformation is identity
const volumeActor = this.getDefaultActor().volumeActor as vtkVolume
const actor = this.getDefaultActor()

if (!actor) {
// Until viewports set up their actors
return worldPos
}

const volumeActor = actor.volumeActor as vtkVolume
const mat = volumeActor.getMatrix()

const p1 = worldPos[0]
Expand All @@ -230,25 +239,39 @@ class Viewport {
* @param direction 0 for horizontal, 1 for vertical
*/
public flip = (direction: number): void => {
const scene = this.getRenderingEngine().getScene(this.sceneUID)

const scale = [1, 1]
scale[direction] *= -1

const actors = this.getActors()
actors.forEach((actor) => {
const volumeActor = actor.volumeActor as vtkVolume
const mat = volumeActor.getUserMatrix()

const actorScale = [mat[0], mat[5], mat[10]]

const tx = vtkMatrixBuilder
.buildFromRadian()
.identity()
.scale(
actorScale[0] * scale[0],
actorScale[1] * scale[1],
actorScale[2]
)
volumeActor.setUserMatrix(tx.getMatrix())
.scale(scale[0], scale[1], 1)

const mat = mat4.create()
mat4.multiply(mat, volumeActor.getUserMatrix(), tx.getMatrix())
volumeActor.setUserMatrix(mat)

this.getRenderingEngine().render()

if (scene) {
// If volume viewport
const viewports = scene.getViewports()
viewports.forEach((vp) => {
const { focalPoint, position } = vp.getCamera()
tx.apply(focalPoint)
tx.apply(position)
vp.setCamera({
focalPoint,
position,
})
})
}
})

this.getRenderingEngine().render()
Expand Down Expand Up @@ -457,6 +480,16 @@ class Viewport {
// and do the right thing.
renderer.invokeEvent(RESET_CAMERA_EVENT)

const eventDetail = {
camera: this.getCamera(),
canvas: this.canvas,
viewportUID: this.uid,
sceneUID: this.sceneUID,
renderingEngineUID: this.renderingEngineUID,
}

// For crosshairs to adapt to new viewport size
triggerEvent(this.canvas, Events.CAMERA_MODIFIED, eventDetail)
return true
}
// */
Expand Down Expand Up @@ -498,8 +531,16 @@ class Viewport {
// how useful is this without the renderer context?
// Lets add it back if we find we need it.
//compositeProjectionMatrix: vtkCamera.getCompositeProjectionMatrix(),
position: <Point3>vtkCamera.getPosition(),
focalPoint: <Point3>vtkCamera.getFocalPoint(),
//
//
// Compensating for the flipped viewport. Since our method for flipping is
// flipping the actor matrix itself, the focal point won't change; therefore,
// we need to accomodate for this required change elsewhere
// vec3.sub(dir, viewport.applyFlipTx(focalPoint), point)
position: <Point3>this.applyFlipTx(vtkCamera.getPosition() as Point3),
focalPoint: <Point3>this.applyFlipTx(vtkCamera.getFocalPoint() as Point3),
// position: <Point3>vtkCamera.getPosition(),
// focalPoint: <Point3>vtkCamera.getFocalPoint(),
parallelProjection: vtkCamera.getParallelProjection(),
parallelScale: vtkCamera.getParallelScale(),
viewAngle: vtkCamera.getViewAngle(),
Expand Down Expand Up @@ -539,11 +580,11 @@ class Viewport {
}

if (position !== undefined) {
vtkCamera.setPosition(...position)
vtkCamera.setPosition(...this.applyFlipTx(position))
}

if (focalPoint !== undefined) {
vtkCamera.setFocalPoint(...focalPoint)
vtkCamera.setFocalPoint(...this.applyFlipTx(focalPoint))
}

if (parallelScale !== undefined) {
Expand Down
14 changes: 7 additions & 7 deletions packages/cornerstone-render/src/constants/orientation.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Orientation } from '../types'
import { Orientation, Point3 } from '../types'

/**
* Convenient reference values often used to set a specific orientation
Expand Down Expand Up @@ -34,16 +34,16 @@ import { Orientation } from '../types'
*/
const ORIENTATION: Record<string, Orientation> = {
AXIAL: {
sliceNormal: [0, 0, -1],
viewUp: [0, -1, 0],
sliceNormal: <Point3>[0, 0, -1],
viewUp: <Point3>[0, -1, 0],
},
SAGITTAL: {
sliceNormal: [1, 0, 0],
viewUp: [0, 0, 1],
sliceNormal: <Point3>[1, 0, 0],
viewUp: <Point3>[0, 0, 1],
},
CORONAL: {
sliceNormal: [0, 1, 0],
viewUp: [0, 0, 1],
sliceNormal: <Point3>[0, 1, 0],
viewUp: <Point3>[0, 0, 1],
},
}

Expand Down
6 changes: 4 additions & 2 deletions packages/cornerstone-render/src/types/Orientation.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { Point3 } from '../types'

type Orientation = {
sliceNormal: Array<number>
viewUp: Array<number>
sliceNormal: Point3
viewUp: Point3
}

export default Orientation
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import Orientation from './Orientation'
*/
type ViewportInputOptions = {
background?: Array<number>
orientation: Orientation
orientation?: Orientation
}

export default ViewportInputOptions
10 changes: 5 additions & 5 deletions packages/cornerstone-tools/src/drawingSvg/drawTextBox.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ function _drawTextGroup(
const { padding, color, fontFamily, fontSize, background } = options

let textGroupBoundingBox
const [x, y] = [position[0] - padding, position[1] - padding]
const [x, y] = [position[0] + padding, position[1] + padding]
const svgns = 'http://www.w3.org/2000/svg'
const svgNodeHash = _getHash(toolUID, annotationUID, 'text', textUID)
const existingTextGroup = svgDrawingHelper._getSvgNode(svgNodeHash)
Expand All @@ -80,8 +80,8 @@ function _drawTextGroup(
const attributes = {
fill: color,
'font-size': fontSize,
'font-family': fontFamily
};
'font-family': fontFamily,
}

_setNewAttributesIfValid(attributes, textElement)

Expand Down Expand Up @@ -163,7 +163,7 @@ function _drawTextBackground(group: SVGGElement, color: string) {
group.removeChild(element)
}

return group.getBBox();
return group.getBBox()
}

// Otherwise, check if we have a <rect> element. If not, create one
Expand All @@ -181,7 +181,7 @@ function _drawTextBackground(group: SVGGElement, color: string) {
element.setAttribute('height', `${bBox.height}`)
element.setAttribute('fill', color)

return bBox;
return bBox
}

export default drawTextBox
88 changes: 44 additions & 44 deletions packages/cornerstone-tools/src/tools/CrosshairsTool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -358,6 +358,50 @@ export default class CrosshairsTool extends BaseAnnotationTool {
return isCrosshairsActive
}

_initCrosshairs = (evt, toolState) => {
const eventData = evt.detail
const { canvas: element } = eventData
const enabledElement = getEnabledElement(element)
const { viewport, FrameOfReferenceUID, viewportUID, sceneUID } =
enabledElement
const { sHeight, sWidth, canvasToWorld } = viewport
const centerCanvas: Point2 = [sWidth * 0.5, sHeight * 0.5]

// Calculate the crosshair center
// NOTE: it is assumed that all the active/linked viewports share the same crosshair center.
// This because the rotation operations rotates also all the other active/intersecting reference lines of the same angle
this.toolCenter = canvasToWorld(centerCanvas)

const camera = viewport.getCamera()
const { position, focalPoint } = camera

const toolData = {
metadata: {
cameraPosition: <Point3>[...position],
cameraFocalPoint: <Point3>[...focalPoint],
FrameOfReferenceUID,
toolName: this.name,
},
data: {
handles: {
rotationPoints: [], // rotation handles, used for rotation interactions
slabThicknessPoints: [], // slab thickness handles, used for setting the slab thickness
},
active: false,
activeOperation: null, // 0 translation, 1 rotation handles, 2 slab thickness handles
activeViewportUIDs: [], // a list of the viewport uids connected to the reference lines being translated
viewportUID,
sceneUID,
},
}

// NOTE: rotation handles are initialized in renderTool when drawing.

addToolState(element, toolData)

showToolCursor(element)
}

onCameraModified = (evt) => {
const eventData = evt.detail
const { canvas: element } = eventData
Expand Down Expand Up @@ -1514,50 +1558,6 @@ export default class CrosshairsTool extends BaseAnnotationTool {
return otherViewportsToolDataWithUniqueCameras
}

_initCrosshairs = (evt, toolState) => {
const eventData = evt.detail
const { canvas: element } = eventData
const enabledElement = getEnabledElement(element)
const { viewport, FrameOfReferenceUID, viewportUID, sceneUID } =
enabledElement
const { sHeight, sWidth, canvasToWorld } = viewport
const centerCanvas: Point2 = [sWidth * 0.5, sHeight * 0.5]

// Calculate the crosshair center
// NOTE: it is assumed that all the active/linked viewports share the same crosshair center.
// This because the rotation operations rotates also all the other active/intersecting reference lines of the same angle
this.toolCenter = canvasToWorld(centerCanvas)

const camera = viewport.getCamera()
const { position, focalPoint } = camera

const toolData = {
metadata: {
cameraPosition: <Point3>[...position],
cameraFocalPoint: <Point3>[...focalPoint],
FrameOfReferenceUID,
toolName: this.name,
},
data: {
handles: {
rotationPoints: [], // rotation handles, used for rotation interactions
slabThicknessPoints: [], // slab thickness handles, used for setting the slab thickness
},
active: false,
activeOperation: null, // 0 translation, 1 rotation handles, 2 slab thickness handles
activeViewportUIDs: [], // a list of the viewport uids connected to the reference lines being translated
viewportUID,
sceneUID,
},
}

// NOTE: rotation handles are initialized in renderTool when drawing.

addToolState(element, toolData)

showToolCursor(element)
}

_jump = (enabledElement, jumpWorld) => {
state.isToolLocked = true

Expand Down
10 changes: 6 additions & 4 deletions packages/demo/src/ExampleFlipViewport.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ import {
removeToolStateByToolUID,
} from '@ohif/cornerstone-tools'

import vtkConstants from 'vtk.js/Sources/Rendering/Core/VolumeMapper/Constants'


import getImageIds from './helpers/getImageIds'
import ViewportGrid from './components/ViewportGrid'
import { initToolGroups, destroyToolGroups } from './initToolGroups'
Expand All @@ -47,6 +50,7 @@ import getToolDetailForDisplay from './helpers/getToolDetailForDisplay'

const VOLUME = 'volume'
const STACK = 'stack'
const { BlendMode } = vtkConstants

window.cache = cache

Expand All @@ -56,9 +60,7 @@ let ctSceneToolGroup,
stackDXViewportToolGroup,
ptSceneToolGroup

const toolsToUse = PET_CT_ANNOTATION_TOOLS.filter(
(tool) => tool !== 'Crosshairs'
)
const toolsToUse = PET_CT_ANNOTATION_TOOLS
const ctLayoutTools = ['Levels'].concat(toolsToUse)
let viewportInput
class FlipViewportExample extends Component {
Expand Down Expand Up @@ -238,6 +240,7 @@ class FlipViewportExample extends Component {
{
volumeUID: ctVolumeUID,
callback: setCTWWWC,
blendMode: BlendMode.MAXIMUM_INTENSITY_BLEND,
},
])

Expand Down Expand Up @@ -404,7 +407,6 @@ class FlipViewportExample extends Component {
<canvas
tabIndex={-1}
ref={(c) => this._canvasNodes.set(i, c)}
onKeyDown={(evt) => this.cancelToolDrawing(evt)}
/>
</div>
))}
Expand Down
Loading

0 comments on commit 35152ea

Please sign in to comment.