Skip to content

Commit

Permalink
fix: cachedStatistics throttling and textBox rendering (#329)
Browse files Browse the repository at this point in the history
* fix: throttling of the cachedStats

* rebased modifications
  • Loading branch information
sedghi authored and swederik committed Mar 22, 2022
1 parent 96623f2 commit 3f296ae
Show file tree
Hide file tree
Showing 8 changed files with 106 additions and 15 deletions.
30 changes: 21 additions & 9 deletions packages/cornerstone-tools/src/drawingSvg/drawTextBox.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import _getHash from './_getHash'
import { Point2 } from '../types'
import _setNewAttributesIfValid from './_setNewAttributesIfValid'
import _setAttributesIfNecessary from './_setAttributesIfNecessary'

/**
* Draws a textBox.
Expand Down Expand Up @@ -63,8 +63,9 @@ function _drawTextGroup(
const svgNodeHash = _getHash(toolName, annotationUID, 'text', textUID)
const existingTextGroup = svgDrawingHelper._getSvgNode(svgNodeHash)

// Todo: right now textBox gets a re-render even if the textBox has not changed
// and evenIf the attributes are not set again since they are the same.
if (existingTextGroup) {
existingTextGroup.setAttribute('transform', `translate(${x} ${y})`)
// TODO: Iterate each node and update color? font-size?
// TODO: Does not support change in # of text lines
const textElement = existingTextGroup.querySelector('text')
Expand All @@ -77,13 +78,19 @@ function _drawTextGroup(
textSpanElement.textContent = text
}

const attributes = {
const textAttributes = {
fill: color,
'font-size': fontSize,
'font-family': fontFamily,
}

_setNewAttributesIfValid(attributes, textElement)
const textGroupAttributes = {
transform: `translate(${x} ${y})`,
}

// Todo: for some reason this does not work to not re-render the textBox
_setAttributesIfNecessary(textAttributes, textElement)
_setAttributesIfNecessary(textGroupAttributes, existingTextGroup)

textGroupBoundingBox = _drawTextBackground(existingTextGroup, background)

Expand Down Expand Up @@ -175,11 +182,16 @@ function _drawTextBackground(group: SVGGElement, color: string) {

// Get the text groups's bounding box and use it to draw the background rectangle
const bBox = group.getBBox()
element.setAttribute('x', `${bBox.x}`)
element.setAttribute('y', `${bBox.y}`)
element.setAttribute('width', `${bBox.width}`)
element.setAttribute('height', `${bBox.height}`)
element.setAttribute('fill', color)

const attributes = {
x: `${bBox.x}`,
y: `${bBox.y}`,
width: `${bBox.width}`,
height: `${bBox.height}`,
fill: color,
}

_setAttributesIfNecessary(attributes, element)

return bBox
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import {
eventTarget,
getRenderingEngine,
} from '@precisionmetrics/cornerstone-render'
import EVENTS from '../enums/CornerstoneTools3DEvents'
import triggerAnnotationRenderForViewportUIDs from '../util/triggerAnnotationRenderForViewportUIDs'

/**
* This is a callback function that is called when a measurement is modified.
* Since we are throttling the cachedStats calculation for annotation tools,
* we need to trigger a final render for the toolData so that the measurement
* textBox is updated.
* Todo: This will trigger all the annotation tools to re-render, although DOM
* will update those that have changed, but more efficient would be to only
* update the changed toolData.
* Todo: A better way is to extract the textBox render logic from the renderToolData
* of all tools and just trigger a render for that (instead of the entire toolData, even if
* no svg update happens since the attributes for handles are the same)
*/
const onMeasurementModified = function (evt) {
const { viewportUID, renderingEngineUID } = evt.detail
const renderingEngine = getRenderingEngine(renderingEngineUID)
triggerAnnotationRenderForViewportUIDs(renderingEngine, [viewportUID])
}

const enable = function () {
eventTarget.addEventListener(
EVENTS.MEASUREMENT_MODIFIED,
onMeasurementModified
)
}

const disable = function () {
eventTarget.removeEventListener(
EVENTS.MEASUREMENT_MODIFIED,
onMeasurementModified
)
}

export default {
enable,
disable,
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import measurementSelectionListener from './measurementSelectionListener'
import measurementModifiedListener from './measurementModifiedListener'

export { measurementSelectionListener, measurementModifiedListener }
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { getRenderingEngine } from '@precisionmetrics/cornerstone-render'
import triggerAnnotationRenderForViewportUIDs from '../../util/triggerAnnotationRenderForViewportUIDs'

/**
* This is a callback function that is called when a measurement is modified.
* Since we are throttling the cachedStats calculation for annotation tools,
* we need to trigger a final render for the toolData so that the measurement
* textBox is updated.
* Todo: This will trigger all the annotation tools to re-render, although DOM
* will update those that have changed, but more efficient would be to only
* update the changed toolData.
* Todo: A better way is to extract the textBox render logic from the renderToolData
* of all tools and just trigger a render for that (instead of the entire toolData, even if
* no svg update happens since the attributes for handles are the same)
*/
function measurementModifiedListener(evt): void {
const { viewportUID, renderingEngineUID } = evt.detail
const renderingEngine = getRenderingEngine(renderingEngineUID)
triggerAnnotationRenderForViewportUIDs(renderingEngine, [viewportUID])
}

export default measurementModifiedListener
6 changes: 5 additions & 1 deletion packages/cornerstone-tools/src/eventListeners/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@ import {
segmentationDataModifiedEventListener,
segmentationStateModifiedEventListener,
} from './segmentation'
import { measurementSelectionListener } from './toolStyles'
import {
measurementSelectionListener,
measurementModifiedListener,
} from './annotations'
//import touchEventListeners from './touchEventListeners';

export {
Expand All @@ -15,4 +18,5 @@ export {
segmentationStateModifiedEventListener,
segmentationDataModifiedEventListener,
measurementSelectionListener,
measurementModifiedListener,
}

This file was deleted.

13 changes: 11 additions & 2 deletions packages/cornerstone-tools/src/init.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
measurementSelectionListener,
segmentationDataModifiedEventListener,
segmentationStateModifiedEventListener,
measurementModifiedListener,
} from './eventListeners'

import ToolGroupManager from './store/ToolGroupManager'
Expand Down Expand Up @@ -85,7 +86,7 @@ function _removeCornerstoneEventListeners() {

/**
* It adds an event listener to the event target (the cornerstoneTools object) for
* the selection event.
* the measurement selected and measurement modified events.
*/
function _addCornerstoneToolsEventListeners() {
// Clear any listeners that may already be set
Expand All @@ -94,6 +95,7 @@ function _addCornerstoneToolsEventListeners() {
const selectionEvent = TOOLS_EVENTS.MEASUREMENT_SELECTION_CHANGE
const segmentationDataModified = TOOLS_EVENTS.SEGMENTATION_DATA_MODIFIED
const segmentationStateModified = TOOLS_EVENTS.SEGMENTATION_STATE_MODIFIED
const modifiedEvent = TOOLS_EVENTS.MEASUREMENT_MODIFIED

eventTarget.addEventListener(selectionEvent, measurementSelectionListener)
eventTarget.addEventListener(
Expand All @@ -104,13 +106,17 @@ function _addCornerstoneToolsEventListeners() {
segmentationStateModified,
segmentationStateModifiedEventListener
)

eventTarget.addEventListener(selectionEvent, measurementSelectionListener)
eventTarget.addEventListener(modifiedEvent, measurementModifiedListener)
}

/**
* Remove the event listener for the selection event
* Remove the event listener for the the measurement selected and measurement modified events.
*/
function _removeCornerstoneToolsEventListeners() {
const selectionEvent = TOOLS_EVENTS.MEASUREMENT_SELECTION_CHANGE
const modifiedEvent = TOOLS_EVENTS.MEASUREMENT_MODIFIED
const segmentationDataModified = TOOLS_EVENTS.SEGMENTATION_DATA_MODIFIED
const segmentationStateModified = TOOLS_EVENTS.SEGMENTATION_STATE_MODIFIED

Expand All @@ -123,6 +129,9 @@ function _removeCornerstoneToolsEventListeners() {
segmentationStateModified,
segmentationStateModifiedEventListener
)

eventTarget.removeEventListener(selectionEvent, measurementSelectionListener)
eventTarget.removeEventListener(modifiedEvent, measurementModifiedListener)
}

export default init

0 comments on commit 3f296ae

Please sign in to comment.