From 45d660636b0f968e8b6a8b3eb87238d0d9eb1f22 Mon Sep 17 00:00:00 2001 From: Bruno Alves de Faria Date: Thu, 14 Sep 2017 17:42:21 -0300 Subject: [PATCH] PWV-138: Switching selected handle from circle to highlighted line --- .../client/compatibility/bidirectionalTool.js | 55 ++++++++++++---- .../getLongestAndShortestDiameters.js | 16 +++++ .../lib/bidirectional/getSelectedHandleKey.js | 14 ++++ .../client/lib/bidirectional/index.js | 23 +++++++ .../repositionBidirectionalArmHandle.js | 65 +++++++++++++++++++ .../ohif-lesiontracker/client/lib/index.js | 3 + 6 files changed, 164 insertions(+), 12 deletions(-) create mode 100644 Packages/ohif-lesiontracker/client/lib/bidirectional/getLongestAndShortestDiameters.js create mode 100644 Packages/ohif-lesiontracker/client/lib/bidirectional/getSelectedHandleKey.js create mode 100644 Packages/ohif-lesiontracker/client/lib/bidirectional/index.js create mode 100644 Packages/ohif-lesiontracker/client/lib/bidirectional/repositionBidirectionalArmHandle.js diff --git a/Packages/ohif-lesiontracker/client/compatibility/bidirectionalTool.js b/Packages/ohif-lesiontracker/client/compatibility/bidirectionalTool.js index 083d5cd79..2bf4f9e75 100644 --- a/Packages/ohif-lesiontracker/client/compatibility/bidirectionalTool.js +++ b/Packages/ohif-lesiontracker/client/compatibility/bidirectionalTool.js @@ -702,6 +702,42 @@ function handleActivator(element, handles, canvasPoint, distanceThreshold=6) { 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 // the hover, moving and selected states function drawHandles(context, eventData, handles, color, options) { @@ -710,7 +746,7 @@ function drawHandles(context, eventData, handles, color, options) { const handle = handles[handleKey]; handle.drawnIndependently = handle.moving; if (handle.selected) { - handle.active = true; + handle.active = handle.hover; } else { if (handle.hover) { handle.active = true; @@ -1080,28 +1116,23 @@ function onImageRendered(e, eventData) { var handleEndCanvas = cornerstone.pixelToCanvas(element, data.handles.end); 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.strokeStyle = lineColor || color; + context.strokeStyle = color; context.lineWidth = strokeWidth; context.moveTo(handleStartCanvas.x, handleStartCanvas.y); context.lineTo(handleEndCanvas.x, handleEndCanvas.y); context.stroke(); // Draw perpendicular line - let perpendicularColor; - if (!data.active && (data.handles.perpendicularStart.selected || data.handles.perpendicularEnd.selected)) { - perpendicularColor = activeColor; - } - drawPerpendicularLine(context, eventData, element, data, perpendicularColor || color, strokeWidth); + drawPerpendicularLine(context, eventData, element, data, color, strokeWidth); // Draw the handles - const handlesColor = lineColor || perpendicularColor || color; + 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 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); diff --git a/Packages/ohif-lesiontracker/client/lib/bidirectional/getLongestAndShortestDiameters.js b/Packages/ohif-lesiontracker/client/lib/bidirectional/getLongestAndShortestDiameters.js new file mode 100644 index 000000000..04cfc5c7e --- /dev/null +++ b/Packages/ohif-lesiontracker/client/lib/bidirectional/getLongestAndShortestDiameters.js @@ -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) + }; +} diff --git a/Packages/ohif-lesiontracker/client/lib/bidirectional/getSelectedHandleKey.js b/Packages/ohif-lesiontracker/client/lib/bidirectional/getSelectedHandleKey.js new file mode 100644 index 000000000..d8fa18f09 --- /dev/null +++ b/Packages/ohif-lesiontracker/client/lib/bidirectional/getSelectedHandleKey.js @@ -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; +} diff --git a/Packages/ohif-lesiontracker/client/lib/bidirectional/index.js b/Packages/ohif-lesiontracker/client/lib/bidirectional/index.js new file mode 100644 index 000000000..796c5003b --- /dev/null +++ b/Packages/ohif-lesiontracker/client/lib/bidirectional/index.js @@ -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 +}; diff --git a/Packages/ohif-lesiontracker/client/lib/bidirectional/repositionBidirectionalArmHandle.js b/Packages/ohif-lesiontracker/client/lib/bidirectional/repositionBidirectionalArmHandle.js new file mode 100644 index 000000000..68a105ba4 --- /dev/null +++ b/Packages/ohif-lesiontracker/client/lib/bidirectional/repositionBidirectionalArmHandle.js @@ -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; +} diff --git a/Packages/ohif-lesiontracker/client/lib/index.js b/Packages/ohif-lesiontracker/client/lib/index.js index 80f3ac5ff..f81e627dd 100644 --- a/Packages/ohif-lesiontracker/client/lib/index.js +++ b/Packages/ohif-lesiontracker/client/lib/index.js @@ -1,6 +1,9 @@ // StudyList-related functions import './studylist/studylistModification.js'; +// Bidirectional tool utility functions +import './bidirectional'; + // Library functions import './TrialCriteriaConstraints.js'; import './MeasurementValidation.js';