From aa54b308dd6de3372e14ffb547bfab78404f1214 Mon Sep 17 00:00:00 2001 From: James Petts Date: Wed, 24 Jun 2020 13:27:53 +0100 Subject: [PATCH] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20Dashed=20rendering=20opt?= =?UTF-8?q?ions=20for=20tools=20with=20lines/handles=20(#1254)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/drawing/drawArrow.js | 7 ++- src/drawing/drawHandles.js | 45 +++++++++---------- .../modules/globalConfigurationModule.js | 1 + src/tools/annotation/AngleTool.js | 17 ++++++- src/tools/annotation/ArrowAnnotateTool.js | 19 ++++++-- src/tools/annotation/BidirectionalTool.js | 1 + src/tools/annotation/CircleRoiTool.js | 21 +++++++-- src/tools/annotation/CobbAngleTool.js | 23 +++++++--- src/tools/annotation/EllipticalRoiTool.js | 19 ++++++-- src/tools/annotation/FreehandRoiTool.js | 44 ++++++++++++------ src/tools/annotation/LengthTool.js | 25 +++++++++-- src/tools/annotation/ProbeTool.js | 16 ++++--- src/tools/annotation/RectangleRoiTool.js | 19 ++++++-- .../bidirectionalTool/renderToolData.js | 27 ++++++++--- 14 files changed, 208 insertions(+), 76 deletions(-) diff --git a/src/drawing/drawArrow.js b/src/drawing/drawArrow.js index f9d3b2a8b..781463d5b 100644 --- a/src/drawing/drawArrow.js +++ b/src/drawing/drawArrow.js @@ -12,9 +12,10 @@ import drawJoinedLines from './drawJoinedLines.js'; * @param {Object} end The end position. * @param {string} color The color of the arrow. * @param {number} lineWidth The width of the arrow line. + * @param {number[]|| undefined} [lineDash] The optional lineDash style. * @returns {undefined} */ -export default function(context, start, end, color, lineWidth) { +export default function(context, start, end, color, lineWidth, lineDash) { // Variables to be used when creating the arrow const headLength = 10; @@ -26,6 +27,10 @@ export default function(context, start, end, color, lineWidth) { lineWidth, }; + if (lineDash) { + options.lineDash = lineDash; + } + drawLine(context, undefined, start, end, options, 'canvas'); options = { color, diff --git a/src/drawing/drawHandles.js b/src/drawing/drawHandles.js index afad6b53d..9b964d5c1 100755 --- a/src/drawing/drawHandles.js +++ b/src/drawing/drawHandles.js @@ -45,30 +45,29 @@ export default function(context, evtDetail, handles, options = {}) { : toolStyle.getToolWidth(); const fillStyle = options.fill; - path( - context, - { - lineWidth, - fillStyle, - }, - context => { - const handleCanvasCoords = external.cornerstone.pixelToCanvas( - element, - handle - ); + const pathOptions = { lineWidth, fillStyle }; - // Handle's radisu, then tool's radius, then default radius - const handleRadius = - handle.radius || options.handleRadius || state.handleRadius; + if (options.lineDash) { + pathOptions.lineDash = options.lineDash; + } + + path(context, pathOptions, context => { + const handleCanvasCoords = external.cornerstone.pixelToCanvas( + element, + handle + ); + + // Handle's radisu, then tool's radius, then default radius + const handleRadius = + handle.radius || options.handleRadius || state.handleRadius; - context.arc( - handleCanvasCoords.x, - handleCanvasCoords.y, - handleRadius, - 0, - 2 * Math.PI - ); - } - ); + context.arc( + handleCanvasCoords.x, + handleCanvasCoords.y, + handleRadius, + 0, + 2 * Math.PI + ); + }); } } diff --git a/src/store/modules/globalConfigurationModule.js b/src/store/modules/globalConfigurationModule.js index 2ac21461e..04e0e7383 100644 --- a/src/store/modules/globalConfigurationModule.js +++ b/src/store/modules/globalConfigurationModule.js @@ -4,6 +4,7 @@ const configuration = { globalToolSyncEnabled: false, showSVGCursors: false, autoResizeViewports: true, + lineDash: [4, 4], }; export default { diff --git a/src/tools/annotation/AngleTool.js b/src/tools/annotation/AngleTool.js index b9472dd10..8cf548664 100644 --- a/src/tools/annotation/AngleTool.js +++ b/src/tools/annotation/AngleTool.js @@ -27,6 +27,7 @@ import triggerEvent from '../../util/triggerEvent.js'; import EVENTS from '../../events.js'; import getPixelSpacing from '../../util/getPixelSpacing'; import throttle from '../../util/throttle'; +import { getModule } from '../../store/index'; /** * @public @@ -46,6 +47,7 @@ export default class AngleTool extends BaseAnnotationTool { svgCursor: angleCursor, configuration: { drawHandles: true, + renderDashed: false, }, }; @@ -137,9 +139,14 @@ export default class AngleTool extends BaseAnnotationTool { renderToolData(evt) { const eventData = evt.detail; const enabledElement = eventData.enabledElement; - const { handleRadius, drawHandlesOnHover } = this.configuration; + const { + handleRadius, + drawHandlesOnHover, + renderDashed, + } = this.configuration; // If we have no toolData for this element, return immediately as there is nothing to do const toolData = getToolState(evt.currentTarget, this.name); + const lineDash = getModule('globalConfiguration').configuration.lineDash; if (!toolData) { return; @@ -174,12 +181,18 @@ export default class AngleTool extends BaseAnnotationTool { data.handles.middle ); + const lineOptions = { color }; + + if (renderDashed) { + lineOptions.lineDash = lineDash; + } + drawJoinedLines( context, eventData.element, data.handles.start, [data.handles.middle, data.handles.end], - { color } + lineOptions ); // Draw the handles diff --git a/src/tools/annotation/ArrowAnnotateTool.js b/src/tools/annotation/ArrowAnnotateTool.js index ebfdc1361..624584fe8 100644 --- a/src/tools/annotation/ArrowAnnotateTool.js +++ b/src/tools/annotation/ArrowAnnotateTool.js @@ -22,6 +22,7 @@ import drawArrow from './../../drawing/drawArrow.js'; import drawHandles from './../../drawing/drawHandles.js'; import { textBoxWidth } from './../../drawing/drawTextBox.js'; import { arrowAnnotateCursor } from '../cursors/index.js'; +import { getModule } from '../../store/index'; /** * @public @@ -41,6 +42,7 @@ export default class ArrowAnnotateTool extends BaseAnnotationTool { drawHandles: false, drawHandlesOnHover: true, arrowFirst: true, + renderDashed: false, }, svgCursor: arrowAnnotateCursor, }; @@ -97,7 +99,11 @@ export default class ArrowAnnotateTool extends BaseAnnotationTool { renderToolData(evt) { const { element, enabledElement } = evt.detail; - const { handleRadius, drawHandlesOnHover } = this.configuration; + const { + handleRadius, + drawHandlesOnHover, + renderDashed, + } = this.configuration; // If we have no toolData for this element, return immediately as there is nothing to do const toolData = getToolState(element, this.name); @@ -112,6 +118,11 @@ export default class ArrowAnnotateTool extends BaseAnnotationTool { const lineWidth = toolStyle.getToolWidth(); + let lineDash; + if (renderDashed) { + lineDash = getModule('globalConfiguration').configuration.lineDash; + } + for (let i = 0; i < toolData.data.length; i++) { const data = toolData.data[i]; @@ -141,7 +152,8 @@ export default class ArrowAnnotateTool extends BaseAnnotationTool { handleEndCanvas, handleStartCanvas, color, - lineWidth + lineWidth, + lineDash ); } else { drawArrow( @@ -149,7 +161,8 @@ export default class ArrowAnnotateTool extends BaseAnnotationTool { handleStartCanvas, handleEndCanvas, color, - lineWidth + lineWidth, + lineDash ); } diff --git a/src/tools/annotation/BidirectionalTool.js b/src/tools/annotation/BidirectionalTool.js index ddcb0447c..cc34d61c1 100644 --- a/src/tools/annotation/BidirectionalTool.js +++ b/src/tools/annotation/BidirectionalTool.js @@ -37,6 +37,7 @@ export default class BidirectionalTool extends BaseAnnotationTool { shadow: '', drawHandles: true, drawHandlesOnHover: true, + renderDashed: false, additionalData: [], }, svgCursor: bidirectionalCursor, diff --git a/src/tools/annotation/CircleRoiTool.js b/src/tools/annotation/CircleRoiTool.js index 68cbea4b5..c769119ad 100644 --- a/src/tools/annotation/CircleRoiTool.js +++ b/src/tools/annotation/CircleRoiTool.js @@ -5,6 +5,7 @@ import BaseAnnotationTool from '../base/BaseAnnotationTool.js'; import { getToolState } from './../../stateManagement/toolState.js'; import toolStyle from './../../stateManagement/toolStyle.js'; import toolColors from './../../stateManagement/toolColors.js'; +import { getModule } from '../../store/index'; // Drawing import { @@ -43,6 +44,9 @@ export default class CircleRoiTool extends BaseAnnotationTool { name: 'CircleRoi', supportedInteractionTypes: ['Mouse', 'Touch'], svgCursor: circleRoiCursor, + configuration: { + renderDashed: false, + }, }; super(props, defaultProps); @@ -164,9 +168,14 @@ export default class CircleRoiTool extends BaseAnnotationTool { const eventData = evt.detail; const { image, element, canvasContext } = eventData; const lineWidth = toolStyle.getToolWidth(); - const { handleRadius, drawHandlesOnHover } = this.configuration; + const { + handleRadius, + drawHandlesOnHover, + renderDashed, + } = this.configuration; const newContext = getNewContext(canvasContext.canvas); const { rowPixelSpacing, colPixelSpacing } = getPixelSpacing(image); + const lineDash = getModule('globalConfiguration').configuration.lineDash; // Meta const seriesModule = @@ -209,15 +218,19 @@ export default class CircleRoiTool extends BaseAnnotationTool { // Calculating the radius where startCanvas is the center of the circle to be drawn const radius = getDistance(startCanvas, endCanvas); + const circleOptions = { color }; + + if (renderDashed) { + circleOptions.lineDash = lineDash; + } + // Draw Circle drawCircle( context, element, data.handles.start, radius, - { - color, - }, + circleOptions, 'pixel' ); diff --git a/src/tools/annotation/CobbAngleTool.js b/src/tools/annotation/CobbAngleTool.js index 3394e3a30..52df04b84 100644 --- a/src/tools/annotation/CobbAngleTool.js +++ b/src/tools/annotation/CobbAngleTool.js @@ -27,6 +27,7 @@ import { cobbAngleCursor } from '../cursors/index.js'; import triggerEvent from '../../util/triggerEvent.js'; import throttle from '../../util/throttle'; import getPixelSpacing from '../../util/getPixelSpacing'; +import { getModule } from '../../store/index'; /** * @public @@ -43,6 +44,7 @@ export default class CobbAngleTool extends BaseAnnotationTool { svgCursor: cobbAngleCursor, configuration: { drawHandles: true, + renderDashed: false, }, }; @@ -159,7 +161,11 @@ export default class CobbAngleTool extends BaseAnnotationTool { renderToolData(evt) { const eventData = evt.detail; - const { handleRadius, drawHandlesOnHover } = this.configuration; + const { + handleRadius, + drawHandlesOnHover, + renderDashed, + } = this.configuration; // If we have no toolData for this element, return immediately as there is nothing to do const toolData = getToolState(evt.currentTarget, this.name); @@ -171,6 +177,7 @@ export default class CobbAngleTool extends BaseAnnotationTool { const context = getNewContext(eventData.canvasContext.canvas); const lineWidth = toolStyle.getToolWidth(); + const lineDash = getModule('globalConfiguration').configuration.lineDash; const font = textStyle.getFont(); for (let i = 0; i < toolData.data.length; i++) { @@ -186,14 +193,18 @@ export default class CobbAngleTool extends BaseAnnotationTool { // Differentiate the color of activation tool const color = toolColors.getColorIfActive(data); + const lineOptions = { color }; + + if (renderDashed) { + lineOptions.lineDash = lineDash; + } + drawLine( context, eventData.element, data.handles.start, data.handles.end, - { - color, - } + lineOptions ); if (data.complete) { @@ -202,9 +213,7 @@ export default class CobbAngleTool extends BaseAnnotationTool { eventData.element, data.handles.start2, data.handles.end2, - { - color, - } + lineOptions ); } diff --git a/src/tools/annotation/EllipticalRoiTool.js b/src/tools/annotation/EllipticalRoiTool.js index 5ab6c69f1..db6c15d87 100644 --- a/src/tools/annotation/EllipticalRoiTool.js +++ b/src/tools/annotation/EllipticalRoiTool.js @@ -28,6 +28,7 @@ import throttle from './../../util/throttle.js'; import { ellipticalRoiCursor } from '../cursors/index.js'; import { getLogger } from '../../util/logger.js'; import getPixelSpacing from '../../util/getPixelSpacing'; +import { getModule } from '../../store/index'; const logger = getLogger('tools:annotation:EllipticalRoiTool'); @@ -47,6 +48,7 @@ export default class EllipticalRoiTool extends BaseAnnotationTool { configuration: { // showMinMax: false, // showHounsfieldUnits: true, + renderDashed: false, }, svgCursor: ellipticalRoiCursor, }; @@ -177,7 +179,12 @@ export default class EllipticalRoiTool extends BaseAnnotationTool { const eventData = evt.detail; const { image, element } = eventData; const lineWidth = toolStyle.getToolWidth(); - const { handleRadius, drawHandlesOnHover } = this.configuration; + const lineDash = getModule('globalConfiguration').configuration.lineDash; + const { + handleRadius, + drawHandlesOnHover, + renderDashed, + } = this.configuration; const context = getNewContext(eventData.canvasContext.canvas); const { rowPixelSpacing, colPixelSpacing } = getPixelSpacing(image); @@ -209,15 +216,19 @@ export default class EllipticalRoiTool extends BaseAnnotationTool { setShadow(context, this.configuration); + const ellipseOptions = { color }; + + if (renderDashed) { + ellipseOptions.lineDash = lineDash; + } + // Draw drawEllipse( context, element, data.handles.start, data.handles.end, - { - color, - }, + ellipseOptions, 'pixel', data.handles.initialRotation ); diff --git a/src/tools/annotation/FreehandRoiTool.js b/src/tools/annotation/FreehandRoiTool.js index 83301f6d6..b2a4b6c05 100644 --- a/src/tools/annotation/FreehandRoiTool.js +++ b/src/tools/annotation/FreehandRoiTool.js @@ -28,6 +28,7 @@ import { freehandRoiCursor } from '../cursors/index.js'; import freehandUtils from '../../util/freehand/index.js'; import { getLogger } from '../../util/logger.js'; import throttle from '../../util/throttle'; +import { getModule } from '../../store/index'; const logger = getLogger('tools:annotation:FreehandRoiTool'); @@ -360,6 +361,8 @@ export default class FreehandRoiTool extends BaseAnnotationTool { // We have tool data for this element - iterate over each one and draw it const context = getNewContext(eventData.canvasContext.canvas); const lineWidth = toolStyle.getToolWidth(); + const { renderDashed } = config; + const lineDash = getModule('globalConfiguration').configuration.lineDash; for (let i = 0; i < toolState.data.length; i++) { const data = toolState.data[i]; @@ -384,25 +387,39 @@ export default class FreehandRoiTool extends BaseAnnotationTool { fillColor = toolColors.getToolColor(); } + let options = { color }; + + if (renderDashed) { + options.lineDash = lineDash; + } + if (data.handles.points.length) { - for (let j = 0; j < data.handles.points.length; j++) { - const lines = [...data.handles.points[j].lines]; - const points = data.handles.points; - - if (j === points.length - 1 && !data.polyBoundingBox) { - // If it's still being actively drawn, keep the last line to - // The mouse location - lines.push(config.mouseLocation.handles.start); - } - drawJoinedLines(context, element, data.handles.points[j], lines, { - color, - }); + const points = data.handles.points; + + drawJoinedLines(context, element, points[0], points, options); + + if (data.polyBoundingBox) { + drawJoinedLines( + context, + element, + points[points.length - 1], + [points[0]], + options + ); + } else { + drawJoinedLines( + context, + element, + points[points.length - 1], + [config.mouseLocation.handles.start], + options + ); } } // Draw handles - const options = { + options = { color, fill: fillColor, }; @@ -1828,6 +1845,7 @@ function defaultFreehandConfiguration() { currentHandle: 0, currentTool: -1, drawHandles: true, + renderDashed: false, }; } diff --git a/src/tools/annotation/LengthTool.js b/src/tools/annotation/LengthTool.js index 041b10ac9..e99e8548e 100644 --- a/src/tools/annotation/LengthTool.js +++ b/src/tools/annotation/LengthTool.js @@ -17,6 +17,7 @@ import { lengthCursor } from '../cursors/index.js'; import { getLogger } from '../../util/logger.js'; import getPixelSpacing from '../../util/getPixelSpacing'; import throttle from '../../util/throttle'; +import { getModule } from '../../store/index'; const logger = getLogger('tools:annotation:LengthTool'); @@ -35,6 +36,7 @@ export default class LengthTool extends BaseAnnotationTool { svgCursor: lengthCursor, configuration: { drawHandles: true, + renderDashed: false, }, }; @@ -137,7 +139,11 @@ export default class LengthTool extends BaseAnnotationTool { renderToolData(evt) { const eventData = evt.detail; - const { handleRadius, drawHandlesOnHover } = this.configuration; + const { + handleRadius, + drawHandlesOnHover, + renderDashed, + } = this.configuration; const toolData = getToolState(evt.currentTarget, this.name); if (!toolData) { @@ -150,6 +156,7 @@ export default class LengthTool extends BaseAnnotationTool { 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]; @@ -164,10 +171,20 @@ export default class LengthTool extends BaseAnnotationTool { 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, { - color, - }); + drawLine( + context, + element, + data.handles.start, + data.handles.end, + lineOptions + ); // Draw the handles const handleOptions = { diff --git a/src/tools/annotation/ProbeTool.js b/src/tools/annotation/ProbeTool.js index 3b4eb8f88..95c327845 100644 --- a/src/tools/annotation/ProbeTool.js +++ b/src/tools/annotation/ProbeTool.js @@ -14,6 +14,7 @@ import calculateSUV from '../../util/calculateSUV.js'; import { probeCursor } from '../cursors/index.js'; import { getLogger } from '../../util/logger.js'; import throttle from '../../util/throttle'; +import { getModule } from '../../store/index'; const logger = getLogger('tools:annotation:ProbeTool'); @@ -33,6 +34,7 @@ export default class ProbeTool extends BaseAnnotationTool { svgCursor: probeCursor, configuration: { drawHandles: true, + renderDashed: false, }, }; @@ -131,7 +133,7 @@ export default class ProbeTool extends BaseAnnotationTool { renderToolData(evt) { const eventData = evt.detail; - const { handleRadius } = this.configuration; + const { handleRadius, renderDashed } = this.configuration; const toolData = getToolState(evt.currentTarget, this.name); if (!toolData) { @@ -142,6 +144,7 @@ export default class ProbeTool extends BaseAnnotationTool { const context = getNewContext(eventData.canvasContext.canvas); const { image, element } = eventData; const fontHeight = textStyle.getFontSize(); + const lineDash = getModule('globalConfiguration').configuration.lineDash; for (let i = 0; i < toolData.data.length; i++) { const data = toolData.data[i]; @@ -155,10 +158,13 @@ export default class ProbeTool extends BaseAnnotationTool { if (this.configuration.drawHandles) { // Draw the handles - drawHandles(context, eventData, data.handles, { - handleRadius, - color, - }); + let handleOptions = { handleRadius, color }; + + if (renderDashed) { + handleOptions.lineDash = lineDash; + } + + drawHandles(context, eventData, data.handles, handleOptions); } // Update textbox stats diff --git a/src/tools/annotation/RectangleRoiTool.js b/src/tools/annotation/RectangleRoiTool.js index f7c64ac7b..7e4e78f89 100644 --- a/src/tools/annotation/RectangleRoiTool.js +++ b/src/tools/annotation/RectangleRoiTool.js @@ -24,6 +24,7 @@ import throttle from './../../util/throttle.js'; import { rectangleRoiCursor } from '../cursors/index.js'; import { getLogger } from '../../util/logger.js'; import getPixelSpacing from '../../util/getPixelSpacing'; +import { getModule } from '../../store/index'; const logger = getLogger('tools:annotation:RectangleRoiTool'); @@ -42,6 +43,7 @@ export default class RectangleRoiTool extends BaseAnnotationTool { supportedInteractionTypes: ['Mouse', 'Touch'], configuration: { drawHandles: true, + renderDashed: false, // showMinMax: false, // showHounsfieldUnits: true }, @@ -165,7 +167,12 @@ export default class RectangleRoiTool extends BaseAnnotationTool { const eventData = evt.detail; const { image, element } = eventData; const lineWidth = toolStyle.getToolWidth(); - const { handleRadius, drawHandlesOnHover } = this.configuration; + const lineDash = getModule('globalConfiguration').configuration.lineDash; + const { + handleRadius, + drawHandlesOnHover, + renderDashed, + } = this.configuration; const context = getNewContext(eventData.canvasContext.canvas); const { rowPixelSpacing, colPixelSpacing } = getPixelSpacing(image); @@ -197,15 +204,19 @@ export default class RectangleRoiTool extends BaseAnnotationTool { setShadow(context, this.configuration); + const rectOptions = { color }; + + if (renderDashed) { + rectOptions.lineDash = lineDash; + } + // Draw drawRect( context, element, data.handles.start, data.handles.end, - { - color, - }, + rectOptions, 'pixel', data.handles.initialRotation ); diff --git a/src/tools/annotation/bidirectionalTool/renderToolData.js b/src/tools/annotation/bidirectionalTool/renderToolData.js index b711fe4aa..3008398d7 100644 --- a/src/tools/annotation/bidirectionalTool/renderToolData.js +++ b/src/tools/annotation/bidirectionalTool/renderToolData.js @@ -1,6 +1,7 @@ /* eslint no-loop-func: 0 */ // --> OFF import drawHandles from './../../../drawing/drawHandles.js'; import updatePerpendicularLineHandles from './utils/updatePerpendicularLineHandles.js'; +import { getModule } from '../../../store/index'; import toolStyle from './../../../stateManagement/toolStyle.js'; import toolColors from './../../../stateManagement/toolColors.js'; @@ -17,7 +18,9 @@ import getPixelSpacing from '../../../util/getPixelSpacing'; export default function(evt) { const eventData = evt.detail; const { element, canvasContext, image } = eventData; - const { handleRadius, drawHandlesOnHover } = this.configuration; + const { handleRadius, drawHandlesOnHover, renderDashed } = this.configuration; + + const lineDash = getModule('globalConfiguration').configuration.lineDash; // If we have no toolData for this element, return immediately as there is nothing to do const toolData = getToolState(element, this.name); @@ -70,17 +73,29 @@ export default function(evt) { textBox, } = data.handles; + const lineOptions = { color }; + const perpendicularLineOptions = { color, strokeWidth }; + + if (renderDashed) { + lineOptions.lineDash = lineDash; + perpendicularLineOptions.lineDash = lineDash; + } + // Draw the measurement line - drawLine(context, element, start, end, { color }); + drawLine(context, element, start, end, lineOptions); // Draw perpendicular line const strokeWidth = lineWidth; updatePerpendicularLineHandles(eventData, data); - drawLine(context, element, perpendicularStart, perpendicularEnd, { - color, - strokeWidth, - }); + + drawLine( + context, + element, + perpendicularStart, + perpendicularEnd, + perpendicularLineOptions + ); // Draw the handles const handleOptions = {