PWV-138: Switching selected handle from circle to highlighted line
This commit is contained in:
parent
8c6a72cc0f
commit
45d660636b
@ -702,6 +702,42 @@ function handleActivator(element, handles, canvasPoint, distanceThreshold=6) {
|
|||||||
return handleActivatorChanged;
|
return handleActivatorChanged;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Draw a line marker over the selected arm
|
||||||
|
function drawSelectedMarker(eventData, handles, color) {
|
||||||
|
const lib = OHIF.lesiontracker.bidirectional;
|
||||||
|
const { canvasContext, element, enabledElement } = eventData;
|
||||||
|
|
||||||
|
const handleKey = lib.getSelectedHandleKey(handles);
|
||||||
|
if (!handleKey) return;
|
||||||
|
const handle = handles[handleKey];
|
||||||
|
|
||||||
|
// Used a big distance (1km) to fill the entire line
|
||||||
|
const mmStep = -1000000;
|
||||||
|
|
||||||
|
// Get the line's start and end points
|
||||||
|
const fakeImage = {
|
||||||
|
columnPixelSpacing: eventData.viewport.scale,
|
||||||
|
rowPixelSpacing: eventData.viewport.scale
|
||||||
|
};
|
||||||
|
const pointA = lib.repositionBidirectionalArmHandle(fakeImage, handles, handleKey, mmStep, 0);
|
||||||
|
const pointB = _.pick(handle, ['x', 'y']);
|
||||||
|
|
||||||
|
// Stop here if pointA is not present
|
||||||
|
if (!pointA) return;
|
||||||
|
|
||||||
|
// Get the canvas coordinates for the line var perpendicularStartCanvas = cornerstone.pixelToCanvas(element, data.handles.perpendicularStart);
|
||||||
|
const canvasPointA = cornerstone.pixelToCanvas(element, pointA);
|
||||||
|
const canvasPointB = cornerstone.pixelToCanvas(element, pointB);
|
||||||
|
|
||||||
|
// Draw the line marker
|
||||||
|
canvasContext.beginPath();
|
||||||
|
canvasContext.strokeStyle = color;
|
||||||
|
canvasContext.lineWidth = cornerstoneTools.toolStyle.getToolWidth();
|
||||||
|
canvasContext.moveTo(canvasPointA.x, canvasPointA.y);
|
||||||
|
canvasContext.lineTo(canvasPointB.x, canvasPointB.y);
|
||||||
|
canvasContext.stroke();
|
||||||
|
}
|
||||||
|
|
||||||
// Add a proxy to cornerstoneTools.drawHandles function to change the handles' active state base on
|
// Add a proxy to cornerstoneTools.drawHandles function to change the handles' active state base on
|
||||||
// the hover, moving and selected states
|
// the hover, moving and selected states
|
||||||
function drawHandles(context, eventData, handles, color, options) {
|
function drawHandles(context, eventData, handles, color, options) {
|
||||||
@ -710,7 +746,7 @@ function drawHandles(context, eventData, handles, color, options) {
|
|||||||
const handle = handles[handleKey];
|
const handle = handles[handleKey];
|
||||||
handle.drawnIndependently = handle.moving;
|
handle.drawnIndependently = handle.moving;
|
||||||
if (handle.selected) {
|
if (handle.selected) {
|
||||||
handle.active = true;
|
handle.active = handle.hover;
|
||||||
} else {
|
} else {
|
||||||
if (handle.hover) {
|
if (handle.hover) {
|
||||||
handle.active = true;
|
handle.active = true;
|
||||||
@ -1080,28 +1116,23 @@ function onImageRendered(e, eventData) {
|
|||||||
var handleEndCanvas = cornerstone.pixelToCanvas(element, data.handles.end);
|
var handleEndCanvas = cornerstone.pixelToCanvas(element, data.handles.end);
|
||||||
var canvasTextLocation = cornerstone.pixelToCanvas(element, data.handles.textBox);
|
var canvasTextLocation = cornerstone.pixelToCanvas(element, data.handles.textBox);
|
||||||
|
|
||||||
let lineColor;
|
|
||||||
if (!data.active && (data.handles.start.selected || data.handles.end.selected)) {
|
|
||||||
lineColor = activeColor;
|
|
||||||
}
|
|
||||||
context.beginPath();
|
context.beginPath();
|
||||||
context.strokeStyle = lineColor || color;
|
context.strokeStyle = color;
|
||||||
context.lineWidth = strokeWidth;
|
context.lineWidth = strokeWidth;
|
||||||
context.moveTo(handleStartCanvas.x, handleStartCanvas.y);
|
context.moveTo(handleStartCanvas.x, handleStartCanvas.y);
|
||||||
context.lineTo(handleEndCanvas.x, handleEndCanvas.y);
|
context.lineTo(handleEndCanvas.x, handleEndCanvas.y);
|
||||||
context.stroke();
|
context.stroke();
|
||||||
|
|
||||||
// Draw perpendicular line
|
// Draw perpendicular line
|
||||||
let perpendicularColor;
|
drawPerpendicularLine(context, eventData, element, data, color, strokeWidth);
|
||||||
if (!data.active && (data.handles.perpendicularStart.selected || data.handles.perpendicularEnd.selected)) {
|
|
||||||
perpendicularColor = activeColor;
|
|
||||||
}
|
|
||||||
drawPerpendicularLine(context, eventData, element, data, perpendicularColor || color, strokeWidth);
|
|
||||||
|
|
||||||
// Draw the handles
|
// Draw the handles
|
||||||
const handlesColor = lineColor || perpendicularColor || color;
|
const handlesColor = color;
|
||||||
drawHandles(context, eventData, data.handles, handlesColor, { drawHandlesIfActive: true });
|
drawHandles(context, eventData, data.handles, handlesColor, { drawHandlesIfActive: true });
|
||||||
|
|
||||||
|
// Draw the selected marker
|
||||||
|
drawSelectedMarker(eventData, data.handles, '#FF9999');
|
||||||
|
|
||||||
// Calculate the long axis length
|
// Calculate the long axis length
|
||||||
var dx = (data.handles.start.x - data.handles.end.x) * (eventData.image.columnPixelSpacing || 1);
|
var dx = (data.handles.start.x - data.handles.end.x) * (eventData.image.columnPixelSpacing || 1);
|
||||||
var dy = (data.handles.start.y - data.handles.end.y) * (eventData.image.rowPixelSpacing || 1);
|
var dy = (data.handles.start.y - data.handles.end.y) * (eventData.image.rowPixelSpacing || 1);
|
||||||
|
|||||||
@ -0,0 +1,16 @@
|
|||||||
|
export default function(handles, image={}) {
|
||||||
|
// Calculate the long axis length
|
||||||
|
const dx = (handles.start.x - handles.end.x) * (image.columnPixelSpacing || 1);
|
||||||
|
const dy = (handles.start.y - handles.end.y) * (image.rowPixelSpacing || 1);
|
||||||
|
const length = Math.sqrt(dx * dx + dy * dy) || 0;
|
||||||
|
|
||||||
|
// Calculate the short axis length
|
||||||
|
const wx = (handles.perpendicularStart.x - handles.perpendicularEnd.x) * (image.columnPixelSpacing || 1);
|
||||||
|
const wy = (handles.perpendicularStart.y - handles.perpendicularEnd.y) * (image.rowPixelSpacing || 1);
|
||||||
|
const width = Math.sqrt(wx * wx + wy * wy) || 0;
|
||||||
|
|
||||||
|
return {
|
||||||
|
longestDiameter: length.toFixed(1),
|
||||||
|
shortestDiameter: width.toFixed(1)
|
||||||
|
};
|
||||||
|
}
|
||||||
@ -0,0 +1,14 @@
|
|||||||
|
// Get the key for the handle which is selected
|
||||||
|
export default function(handles) {
|
||||||
|
let selectedHandleKey;
|
||||||
|
Object.keys(handles).every(handleKey => {
|
||||||
|
const handle = handles[handleKey];
|
||||||
|
if (handle.selected) {
|
||||||
|
selectedHandleKey = handleKey;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
return selectedHandleKey;
|
||||||
|
}
|
||||||
@ -0,0 +1,23 @@
|
|||||||
|
import { OHIF } from 'meteor/ohif:core';
|
||||||
|
import getSelectedHandleKey from './getSelectedHandleKey';
|
||||||
|
import repositionBidirectionalArmHandle from './repositionBidirectionalArmHandle';
|
||||||
|
import getLongestAndShortestDiameters from './getLongestAndShortestDiameters';
|
||||||
|
|
||||||
|
OHIF.lesiontracker.bidirectional = {
|
||||||
|
toolType: 'bidirectional',
|
||||||
|
inverseKeyMap: {
|
||||||
|
start: 'end',
|
||||||
|
end: 'start',
|
||||||
|
perpendicularStart: 'perpendicularEnd',
|
||||||
|
perpendicularEnd: 'perpendicularStart'
|
||||||
|
},
|
||||||
|
perpendicularKeyMap: {
|
||||||
|
start: 'perpendicularStart',
|
||||||
|
end: 'perpendicularEnd',
|
||||||
|
perpendicularStart: 'start',
|
||||||
|
perpendicularEnd: 'end'
|
||||||
|
},
|
||||||
|
getSelectedHandleKey,
|
||||||
|
repositionBidirectionalArmHandle,
|
||||||
|
getLongestAndShortestDiameters
|
||||||
|
};
|
||||||
@ -0,0 +1,65 @@
|
|||||||
|
import { _ } from 'meteor/underscore';
|
||||||
|
import { OHIF } from 'meteor/ohif:core';
|
||||||
|
|
||||||
|
// Return the newPosition for the handle based on the mmStep and handles
|
||||||
|
export default function(image, handles, handleKey, mmStep, mmLimit=1) {
|
||||||
|
if (handleKey === 'textBox') return;
|
||||||
|
|
||||||
|
const lib = OHIF.lesiontracker.bidirectional;
|
||||||
|
|
||||||
|
// Defines how much the arm will increase/decrease
|
||||||
|
const columnPixelSpacing = (image && image.columnPixelSpacing) || 1;
|
||||||
|
const rowPixelSpacing = (image && image.rowPixelSpacing) || 1;
|
||||||
|
const stepX = mmStep * (1 / columnPixelSpacing);
|
||||||
|
const stepY = mmStep * (1 / rowPixelSpacing);
|
||||||
|
|
||||||
|
// Get the line angle and its handles
|
||||||
|
const keyA = handleKey;
|
||||||
|
const keyB = lib.inverseKeyMap[handleKey];
|
||||||
|
const handleA = handles[keyA];
|
||||||
|
const handleB = handles[keyB];
|
||||||
|
const angle = Math.atan2(handleA.y - handleB.y, handleA.x - handleB.x);
|
||||||
|
|
||||||
|
// Calculate the new position of the handle
|
||||||
|
const newPosition = {
|
||||||
|
x: handleA.x + Math.cos(angle) * stepX,
|
||||||
|
y: handleA.y + Math.sin(angle) * stepY
|
||||||
|
};
|
||||||
|
|
||||||
|
if (mmStep < 0) {
|
||||||
|
// Get the perpendicular handles
|
||||||
|
const keyC = lib.perpendicularKeyMap[keyA];
|
||||||
|
const keyD = lib.perpendicularKeyMap[keyB];
|
||||||
|
const handleC = handles[keyC];
|
||||||
|
const handleD = handles[keyD];
|
||||||
|
|
||||||
|
// Create the line segment for the arm being resized
|
||||||
|
const lineAB = {
|
||||||
|
start: _.pick(handleA, ['x', 'y']),
|
||||||
|
end: _.pick(handleB, ['x', 'y'])
|
||||||
|
};
|
||||||
|
|
||||||
|
// Create the line segment for the perpendicular arm
|
||||||
|
const lineCD = {
|
||||||
|
start: _.pick(handleC, ['x', 'y']),
|
||||||
|
end: _.pick(handleD, ['x', 'y'])
|
||||||
|
};
|
||||||
|
|
||||||
|
// Get the intersection point between the arms
|
||||||
|
const intersection = cornerstoneMath.lineSegment.intersectLine(lineAB, lineCD);
|
||||||
|
|
||||||
|
// Keep the minimum distance of 0.1 mm to the intersection point
|
||||||
|
const dx = (intersection.x - newPosition.x) * columnPixelSpacing;
|
||||||
|
const dy = (intersection.y - newPosition.y) * rowPixelSpacing;
|
||||||
|
const distance = Math.sqrt((dx * dx) + (dy * dy));
|
||||||
|
const newAngle = Math.atan2(newPosition.y - intersection.y, newPosition.x - intersection.x);
|
||||||
|
if (angle.toFixed(8) !== newAngle.toFixed(8) || distance < Math.abs(mmLimit)) {
|
||||||
|
Object.assign(newPosition, {
|
||||||
|
x: intersection.x - Math.cos(angle) * mmLimit * Math.sign(stepX),
|
||||||
|
y: intersection.y - Math.sin(angle) * mmLimit * Math.sign(stepY)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return newPosition;
|
||||||
|
}
|
||||||
@ -1,6 +1,9 @@
|
|||||||
// StudyList-related functions
|
// StudyList-related functions
|
||||||
import './studylist/studylistModification.js';
|
import './studylist/studylistModification.js';
|
||||||
|
|
||||||
|
// Bidirectional tool utility functions
|
||||||
|
import './bidirectional';
|
||||||
|
|
||||||
// Library functions
|
// Library functions
|
||||||
import './TrialCriteriaConstraints.js';
|
import './TrialCriteriaConstraints.js';
|
||||||
import './MeasurementValidation.js';
|
import './MeasurementValidation.js';
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user