218 lines
8.2 KiB
JavaScript
218 lines
8.2 KiB
JavaScript
import { cornerstone, cornerstoneMath, cornerstoneTools } from 'meteor/ohif:cornerstone';
|
|
import { OHIF } from 'meteor/ohif:core';
|
|
import { toolType } from '../definitions';
|
|
import drawHandles from './drawHandles';
|
|
import updatePerpendicularLineHandles from '../updatePerpendicularLineHandles';
|
|
import drawPerpendicularLine from './drawPerpendicularLine';
|
|
import drawSelectedMarker from './drawSelectedMarker';
|
|
|
|
export default function(event) {
|
|
const eventData = event.detail;
|
|
const { element, canvasContext } = eventData;
|
|
|
|
// if we have no toolData for this element, return immediately as there is nothing to do
|
|
const toolData = cornerstoneTools.getToolState(element, toolType);
|
|
if (!toolData) return;
|
|
|
|
const imagePlane = cornerstone.metaData.get('imagePlaneModule', eventData.image.imageId);
|
|
let rowPixelSpacing;
|
|
let colPixelSpacing;
|
|
|
|
if (imagePlane) {
|
|
rowPixelSpacing = imagePlane.rowPixelSpacing || imagePlane.rowImagePixelSpacing;
|
|
colPixelSpacing = imagePlane.columnPixelSpacing || imagePlane.colImagePixelSpacing;
|
|
} else {
|
|
rowPixelSpacing = eventData.image.rowPixelSpacing;
|
|
colPixelSpacing = eventData.image.columnPixelSpacing;
|
|
}
|
|
|
|
// LT-29 Disable Target Measurements when pixel spacing is not available
|
|
if (!rowPixelSpacing || !colPixelSpacing) {
|
|
return;
|
|
}
|
|
|
|
// we have tool data for this element - iterate over each one and draw it
|
|
const context = canvasContext.canvas.getContext('2d');
|
|
context.setTransform(1, 0, 0, 1, 0, 0);
|
|
|
|
let color;
|
|
const lineWidth = cornerstoneTools.toolStyle.getToolWidth();
|
|
const config = cornerstoneTools[toolType].getConfiguration();
|
|
|
|
for (let i = 0; i < toolData.data.length; i++) {
|
|
const data = toolData.data[i];
|
|
if (data.visible === false) {
|
|
continue;
|
|
}
|
|
|
|
const { start, end, perpendicularStart, perpendicularEnd, textBox } = data.handles;
|
|
const strokeWidth = lineWidth;
|
|
|
|
context.save();
|
|
|
|
// configurable shadow from CornerstoneTools
|
|
const { shadow } = config;
|
|
if (shadow && shadow.shadow) {
|
|
context.shadowColor = shadow.shadowColor || '#000000';
|
|
context.shadowOffsetX = shadow.shadowOffsetX || 1;
|
|
context.shadowOffsetY = shadow.shadowOffsetY || 1;
|
|
}
|
|
|
|
const activeColor = cornerstoneTools.toolColors.getActiveColor();
|
|
if (data.active) {
|
|
color = activeColor;
|
|
} else {
|
|
color = cornerstoneTools.toolColors.getToolColor();
|
|
}
|
|
|
|
// Update the perpendicular handles to draw it correctly
|
|
updatePerpendicularLineHandles(eventData, data);
|
|
|
|
// Draw the line
|
|
const { pixelToCanvas } = cornerstone;
|
|
const handleStartCanvas = pixelToCanvas(element, start);
|
|
const handleEndCanvas = pixelToCanvas(element, end);
|
|
const handlePerpendicularStartCanvas = pixelToCanvas(element, perpendicularStart);
|
|
const handlePerpendicularEndCanvas = pixelToCanvas(element, perpendicularEnd);
|
|
const canvasTextLocation = pixelToCanvas(element, textBox);
|
|
|
|
context.beginPath();
|
|
context.strokeStyle = color;
|
|
context.lineWidth = strokeWidth;
|
|
context.moveTo(handleStartCanvas.x, handleStartCanvas.y);
|
|
context.lineTo(handleEndCanvas.x, handleEndCanvas.y);
|
|
context.stroke();
|
|
|
|
// Draw perpendicular line
|
|
drawPerpendicularLine(context, element, data, color, strokeWidth);
|
|
|
|
// Draw the handles
|
|
const handlesColor = color;
|
|
drawHandles(context, eventData, data.handles, handlesColor, { drawHandlesIfActive: true });
|
|
|
|
// Draw the selected marker
|
|
drawSelectedMarker(eventData, data.handles, '#FF9999');
|
|
|
|
// Calculate the long axis length
|
|
const dx = (start.x - end.x) * (colPixelSpacing || 1);
|
|
const dy = (start.y - end.y) * (rowPixelSpacing || 1);
|
|
let length = Math.sqrt(dx * dx + dy * dy);
|
|
|
|
// Calculate the short axis length
|
|
const wx = (perpendicularStart.x - perpendicularEnd.x) * (colPixelSpacing || 1);
|
|
const wy = (perpendicularStart.y - perpendicularEnd.y) * (rowPixelSpacing || 1);
|
|
let width = Math.sqrt(wx * wx + wy * wy);
|
|
if (!width) {
|
|
width = 0;
|
|
}
|
|
|
|
// Length is always longer than width
|
|
if (width > length) {
|
|
const tempW = width;
|
|
const tempL = length;
|
|
length = tempW;
|
|
width = tempL;
|
|
}
|
|
|
|
if (data.measurementNumber) {
|
|
// Draw the textbox
|
|
let suffix = ' mm';
|
|
if (!rowPixelSpacing || !colPixelSpacing) {
|
|
suffix = ' pixels';
|
|
}
|
|
|
|
const lengthText = ' L ' + length.toFixed(1) + suffix;
|
|
const widthText = ' W ' + width.toFixed(1) + suffix;
|
|
const textLines = [`Target ${data.measurementNumber}`, lengthText, widthText];
|
|
|
|
const boundingBox = cornerstoneTools.drawTextBox(
|
|
context,
|
|
textLines,
|
|
canvasTextLocation.x,
|
|
canvasTextLocation.y,
|
|
color,
|
|
config.textBox
|
|
);
|
|
|
|
textBox.boundingBox = boundingBox;
|
|
|
|
OHIF.cornerstone.repositionTextBox(eventData, data, config.textBox);
|
|
|
|
// Draw linked line as dashed
|
|
const link = {
|
|
start: {},
|
|
end: {}
|
|
};
|
|
|
|
const midpointCanvas = {
|
|
x: (handleStartCanvas.x + handleEndCanvas.x) / 2,
|
|
y: (handleStartCanvas.y + handleEndCanvas.y) / 2,
|
|
};
|
|
|
|
// Check if the perpendicular line has some length (start and end are not equal)
|
|
// Note: this check is needed to prevent NaN value on the intersection result
|
|
const { distance } = cornerstoneMath.point;
|
|
const lineHasLength = distance(perpendicularLine.start, perpendicularLine.end) > 0;
|
|
|
|
// Define the lines intersection point
|
|
let linesIntersection;
|
|
if (lineHasLength) {
|
|
// As the line has length, define it as the intersection between the lines
|
|
const { intersectLine } = cornerstoneMath.lineSegment;
|
|
linesIntersection = intersectLine(longLine, perpendicularLine);
|
|
} else {
|
|
// As the line has no length, the tool is in its start position
|
|
linesIntersection = longLine.start;
|
|
}
|
|
|
|
const points = [
|
|
handleStartCanvas,
|
|
handleEndCanvas,
|
|
handlePerpendicularStartCanvas,
|
|
handlePerpendicularEndCanvas,
|
|
linesIntersection
|
|
];
|
|
|
|
link.end.x = canvasTextLocation.x;
|
|
link.end.y = canvasTextLocation.y;
|
|
|
|
link.start = cornerstoneMath.point.findClosestPoint(points, link.end);
|
|
|
|
const boundingBoxPoints = [ {
|
|
// Top middle point of bounding box
|
|
x: boundingBox.left + boundingBox.width / 2,
|
|
y: boundingBox.top
|
|
}, {
|
|
// Left middle point of bounding box
|
|
x: boundingBox.left,
|
|
y: boundingBox.top + boundingBox.height / 2
|
|
}, {
|
|
// Bottom middle point of bounding box
|
|
x: boundingBox.left + boundingBox.width / 2,
|
|
y: boundingBox.top + boundingBox.height
|
|
}, {
|
|
// Right middle point of bounding box
|
|
x: boundingBox.left + boundingBox.width,
|
|
y: boundingBox.top + boundingBox.height / 2
|
|
},
|
|
];
|
|
|
|
link.end = cornerstoneMath.point.findClosestPoint(boundingBoxPoints, link.start);
|
|
context.beginPath();
|
|
context.strokeStyle = color;
|
|
context.lineWidth = lineWidth;
|
|
context.setLineDash([ 2, 3 ]);
|
|
|
|
context.moveTo(link.start.x, link.start.y);
|
|
context.lineTo(link.end.x, link.end.y);
|
|
context.stroke();
|
|
}
|
|
|
|
// Set measurement text to show lesion table
|
|
data.longestDiameter = length.toFixed(1);
|
|
data.shortestDiameter = width.toFixed(1);
|
|
|
|
context.restore();
|
|
}
|
|
}
|