irc_web/.svn/pristine/31/3107f18895ad2b51d9339eb6180...

303 lines
8.3 KiB
Plaintext

import * as cornerstoneTools from 'cornerstone-tools'
// State
const getToolState = cornerstoneTools.getToolState
const toolStyle = cornerstoneTools.toolStyle
const toolColors = cornerstoneTools.toolColors
// Drawing
const getNewContext = cornerstoneTools.import('drawing/getNewContext')
const draw = cornerstoneTools.import('drawing/draw')
const setShadow = cornerstoneTools.import('drawing/setShadow')
const drawLine = cornerstoneTools.import('drawing/drawLine')
const drawLinkedTextBox = cornerstoneTools.import('drawing/drawLinkedTextBox')
const drawHandles = cornerstoneTools.import('drawing/drawHandles')
// util
const lineSegDistance = cornerstoneTools.import('util/lineSegDistance')
const lengthCursor = cornerstoneTools.import('tools/cursors')
const getPixelSpacing = cornerstoneTools.import('util/getPixelSpacing')
const throttle = cornerstoneTools.import('util/throttle')
const getModule = cornerstoneTools.getModule
/**
* @public
* @class LengthTool
* @memberof Tools.Annotation
* @classdesc Tool for measuring distances.
* @extends Tools.Base.BaseAnnotationTool
*/
export default class LengthTool extends cornerstoneTools.LengthTool {
constructor(props = {}) {
const defaultProps = {
name: 'Length',
supportedInteractionTypes: ['Mouse', 'Touch'],
svgCursor: lengthCursor,
configuration: {
drawHandles: true,
drawHandlesOnHover: false,
hideHandlesIfMoving: false,
renderDashed: false,
digits: 2
}
}
super(props, defaultProps)
this.throttledUpdateCachedStats = throttle(this.updateCachedStats, 0)
}
createNewMeasurement(eventData) {
const goodEventData =
eventData && eventData.currentPoints && eventData.currentPoints.image
if (!goodEventData) {
console.log(
`required eventData not supplied to tool ${this.name}'s createNewMeasurement`
)
return
}
const { x, y } = eventData.currentPoints.image
return {
visible: true,
active: true,
color: undefined,
invalidated: true,
handles: {
start: {
x,
y,
highlight: true,
active: false
},
end: {
x,
y,
highlight: true,
active: true
},
textBox: {
active: false,
hasMoved: false,
movesIndependently: false,
drawnIndependently: true,
allowedOutsideImage: true,
hasBoundingBox: true
}
}
}
}
/**
*
*
* @param {*} element
* @param {*} data
* @param {*} coords
* @returns {Boolean}
*/
pointNearTool(element, data, coords) {
const hasStartAndEndHandles =
data && data.handles && data.handles.start && data.handles.end
const validParameters = hasStartAndEndHandles
if (!validParameters) {
console.log(
`invalid parameters supplied to tool ${this.name}'s pointNearTool`
)
return false
}
if (data.visible === false) {
return false
}
return (
lineSegDistance(element, data.handles.start, data.handles.end, coords) <
25
)
}
updateCachedStats(image, element, data) {
const {
digits
} = this.configuration
const { rowPixelSpacing, colPixelSpacing } = getPixelSpacing(image)
// Set rowPixelSpacing and columnPixelSpacing to 1 if they are undefined (or zero)
const dx =
(data.handles.end.x - data.handles.start.x) * (colPixelSpacing || 1)
const dy =
(data.handles.end.y - data.handles.start.y) * (rowPixelSpacing || 1)
// Calculate the length, and create the text variable with the millimeters or pixels suffix
const length = Math.sqrt(dx * dx + dy * dy)
// Store the length inside the tool for outside access
data.length = length.toFixed(digits)
data.invalidated = false
}
renderToolData(evt) {
const eventData = evt.detail
const {
handleRadius,
drawHandlesOnHover,
hideHandlesIfMoving,
renderDashed,
digits
} = this.configuration
const toolData = getToolState(evt.currentTarget, this.name)
if (!toolData) {
return
}
// We have tool data for this element - iterate over each one and draw it
const context = getNewContext(eventData.canvasContext.canvas)
const { image, element } = eventData
const { rowPixelSpacing, colPixelSpacing } = getPixelSpacing(image)
const lineWidth = toolStyle.getToolWidth()
const lineDash = getModule('globalConfiguration').configuration.lineDash
for (let i = 0; i < toolData.data.length; i++) {
const data = toolData.data[i]
if (data.visible === false) {
continue
}
draw(context, context => {
// Configurable shadow
setShadow(context, this.configuration)
const color = toolColors.getColorIfActive(data)
const lineOptions = { color }
if (renderDashed) {
lineOptions.lineDash = lineDash
}
// Draw the measurement line
drawLine(
context,
element,
data.handles.start,
data.handles.end,
lineOptions
)
// Draw the handles
const handleOptions = {
color,
handleRadius,
drawHandlesIfActive: drawHandlesOnHover,
hideHandlesIfMoving
}
if (this.configuration.drawHandles) {
drawHandles(context, eventData, data.handles, handleOptions)
}
if (!data.handles.textBox.hasMoved) {
const coords = {
x: Math.max(data.handles.start.x, data.handles.end.x)
}
// Depending on which handle has the largest x-value,
// Set the y-value for the text box
if (coords.x === data.handles.start.x) {
coords.y = data.handles.start.y
} else {
coords.y = data.handles.end.y
}
data.handles.textBox.x = coords.x
data.handles.textBox.y = coords.y
}
// Move the textbox slightly to the right and upwards
// So that it sits beside the length tool handle
const xOffset = 10
// Update textbox stats
if (data.invalidated === true) {
if (data.length) {
this.throttledUpdateCachedStats(image, element, data)
} else {
this.updateCachedStats(image, element, data)
}
}
const text = []
if (data.hasOwnProperty('remark')) {
if (data.hasOwnProperty('status') && data.status) {
text.push(`${data.remark}(${data.status})`)
} else {
text.push(data.remark)
}
}
text.push(textBoxText(data, rowPixelSpacing, colPixelSpacing))
drawLinkedTextBox(
context,
element,
data.handles.textBox,
text,
data.handles,
textBoxAnchorPoints,
color,
lineWidth,
xOffset,
true
)
})
}
// - SideEffect: Updates annotation 'suffix'
function textBoxText(annotation, rowPixelSpacing, colPixelSpacing) {
const measuredValue = _sanitizeMeasuredValue(annotation.length)
// Measured value is not defined, return empty string
if (!measuredValue) {
return ''
}
// Set the length text suffix depending on whether or not pixelSpacing is available
let suffix = 'mm'
if (!rowPixelSpacing || !colPixelSpacing) {
suffix = 'pixels'
}
annotation.unit = suffix
return `${measuredValue.toFixed(digits)} ${suffix}`
}
function textBoxAnchorPoints(handles) {
const midpoint = {
x: (handles.start.x + handles.end.x) / 2,
y: (handles.start.y + handles.end.y) / 2
}
return [handles.start, midpoint, handles.end]
}
}
}
/**
* Attempts to sanitize a value by casting as a number; if unable to cast,
* we return `undefined`
*
* @param {*} value
* @returns a number or undefined
*/
function _sanitizeMeasuredValue(value) {
const parsedValue = Number(value)
const isNumber = !isNaN(parsedValue)
return isNumber ? parsedValue : undefined
}