From 0ca96533d253c35534c9820e4174b54270483d5e Mon Sep 17 00:00:00 2001 From: Alireza Date: Wed, 23 Nov 2022 10:09:04 -0500 Subject: [PATCH 01/25] fix: mouse-up should not unhighlight annotations (#305) * fix: annotation highlighted and tooling for ellipticalROI * update build * fix tests --- common/reviews/api/tools.api.md | 2 +- packages/tools/src/drawingSvg/drawEllipse.ts | 7 ++++++- packages/tools/src/tools/CrosshairsTool.ts | 1 - packages/tools/src/tools/annotation/AngleTool.ts | 1 - packages/tools/src/tools/annotation/ArrowAnnotateTool.ts | 1 - packages/tools/src/tools/annotation/BidirectionalTool.ts | 1 - packages/tools/src/tools/annotation/EllipticalROITool.ts | 8 +++++++- packages/tools/src/tools/annotation/LengthTool.ts | 1 - packages/tools/src/tools/annotation/ProbeTool.ts | 2 -- packages/tools/src/tools/annotation/RectangleROITool.ts | 1 - .../tools/src/tools/segmentation/CircleScissorsTool.ts | 1 - .../tools/src/tools/segmentation/RectangleScissorsTool.ts | 1 - .../tools/src/tools/segmentation/SphereScissorsTool.ts | 1 - packages/tools/test/LengthTool_test.js | 8 ++++---- packages/tools/test/cpu_LengthTool_test.js | 4 ++-- 15 files changed, 20 insertions(+), 20 deletions(-) diff --git a/common/reviews/api/tools.api.md b/common/reviews/api/tools.api.md index d7969e1b3..5aabc1f10 100644 --- a/common/reviews/api/tools.api.md +++ b/common/reviews/api/tools.api.md @@ -1279,7 +1279,7 @@ function drawArrow(svgDrawingHelper: SVGDrawingHelper, annotationUID: string, ar function drawCircle(svgDrawingHelper: SVGDrawingHelper, annotationUID: string, circleUID: string, center: Types_2.Point2, radius: number, options?: {}): void; // @public (undocumented) -function drawEllipse(svgDrawingHelper: SVGDrawingHelper, annotationUID: string, ellipseUID: string, corner1: Types_2.Point2, corner2: Types_2.Point2, options?: {}): void; +function drawEllipse(svgDrawingHelper: SVGDrawingHelper, annotationUID: string, ellipseUID: string, corner1: Types_2.Point2, corner2: Types_2.Point2, options?: {}, dataId?: string): void; // @public (undocumented) function drawHandles(svgDrawingHelper: SVGDrawingHelper, annotationUID: string, handleGroupUID: string, handlePoints: Array, options?: {}): void; diff --git a/packages/tools/src/drawingSvg/drawEllipse.ts b/packages/tools/src/drawingSvg/drawEllipse.ts index c9c0d5eee..1031d7347 100644 --- a/packages/tools/src/drawingSvg/drawEllipse.ts +++ b/packages/tools/src/drawingSvg/drawEllipse.ts @@ -11,7 +11,8 @@ function drawEllipse( ellipseUID: string, corner1: Types.Point2, corner2: Types.Point2, - options = {} + options = {}, + dataId = '' ): void { const { color, width, lineWidth, lineDash } = Object.assign( { @@ -57,6 +58,10 @@ function drawEllipse( } else { const svgEllipseElement = document.createElementNS(svgns, 'ellipse'); + if (dataId !== '') { + svgEllipseElement.setAttribute('data-id', dataId); + } + _setNewAttributesIfValid(attributes, svgEllipseElement); svgDrawingHelper.appendNode(svgEllipseElement, svgNodeHash); diff --git a/packages/tools/src/tools/CrosshairsTool.ts b/packages/tools/src/tools/CrosshairsTool.ts index 1ecfdc224..9d7ca5150 100644 --- a/packages/tools/src/tools/CrosshairsTool.ts +++ b/packages/tools/src/tools/CrosshairsTool.ts @@ -1924,7 +1924,6 @@ class CrosshairsTool extends AnnotationTool { const eventDetail = evt.detail; const { element } = eventDetail; - this.editData.annotation.highlighted = false; this.editData.annotation.data.handles.activeOperation = null; this.editData.annotation.data.activeViewportIds = []; diff --git a/packages/tools/src/tools/annotation/AngleTool.ts b/packages/tools/src/tools/annotation/AngleTool.ts index ba0c9a538..53dd6d89a 100644 --- a/packages/tools/src/tools/annotation/AngleTool.ts +++ b/packages/tools/src/tools/annotation/AngleTool.ts @@ -343,7 +343,6 @@ class AngleTool extends AnnotationTool { } this.angleStartedNotYetCompleted = false; - annotation.highlighted = false; data.handles.activeHandleIndex = null; this._deactivateModify(element); diff --git a/packages/tools/src/tools/annotation/ArrowAnnotateTool.ts b/packages/tools/src/tools/annotation/ArrowAnnotateTool.ts index eff55d35e..38d130b01 100644 --- a/packages/tools/src/tools/annotation/ArrowAnnotateTool.ts +++ b/packages/tools/src/tools/annotation/ArrowAnnotateTool.ts @@ -306,7 +306,6 @@ class ArrowAnnotateTool extends AnnotationTool { return; } - annotation.highlighted = false; data.handles.activeHandleIndex = null; this._deactivateModify(element); diff --git a/packages/tools/src/tools/annotation/BidirectionalTool.ts b/packages/tools/src/tools/annotation/BidirectionalTool.ts index 049160dea..5705a0b5d 100644 --- a/packages/tools/src/tools/annotation/BidirectionalTool.ts +++ b/packages/tools/src/tools/annotation/BidirectionalTool.ts @@ -404,7 +404,6 @@ class BidirectionalTool extends AnnotationTool { return; } - annotation.highlighted = false; data.handles.activeHandleIndex = null; this._deactivateModify(element); diff --git a/packages/tools/src/tools/annotation/EllipticalROITool.ts b/packages/tools/src/tools/annotation/EllipticalROITool.ts index ce13ea765..10df4d271 100644 --- a/packages/tools/src/tools/annotation/EllipticalROITool.ts +++ b/packages/tools/src/tools/annotation/EllipticalROITool.ts @@ -422,6 +422,10 @@ class EllipticalROITool extends AnnotationTool { return; } + // Elliptical ROI tool should reset its highlight to false on mouse up (as opposed + // to other tools that keep it highlighted until the user moves. The reason + // is that we use top-left and bottom-right handles to define the ellipse, + // and they are by definition not in the ellipse on mouse up. annotation.highlighted = false; data.handles.activeHandleIndex = null; @@ -868,6 +872,7 @@ class EllipticalROITool extends AnnotationTool { ); } + const dataId = `${annotationUID}-ellipse`; const ellipseUID = '0'; drawEllipseSvg( svgDrawingHelper, @@ -879,7 +884,8 @@ class EllipticalROITool extends AnnotationTool { color, lineDash, lineWidth, - } + }, + dataId ); // draw center point, if "centerPointRadius" configuration is valid. diff --git a/packages/tools/src/tools/annotation/LengthTool.ts b/packages/tools/src/tools/annotation/LengthTool.ts index 7084760a3..d1d7770ff 100644 --- a/packages/tools/src/tools/annotation/LengthTool.ts +++ b/packages/tools/src/tools/annotation/LengthTool.ts @@ -347,7 +347,6 @@ class LengthTool extends AnnotationTool { return; } - annotation.highlighted = false; data.handles.activeHandleIndex = null; this._deactivateModify(element); diff --git a/packages/tools/src/tools/annotation/ProbeTool.ts b/packages/tools/src/tools/annotation/ProbeTool.ts index fc5f65c40..e2adbe1cb 100644 --- a/packages/tools/src/tools/annotation/ProbeTool.ts +++ b/packages/tools/src/tools/annotation/ProbeTool.ts @@ -274,8 +274,6 @@ class ProbeTool extends AnnotationTool { const { annotation, viewportIdsToRender, newAnnotation } = this.editData; - annotation.highlighted = false; - const enabledElement = getEnabledElement(element); const { renderingEngine } = enabledElement; diff --git a/packages/tools/src/tools/annotation/RectangleROITool.ts b/packages/tools/src/tools/annotation/RectangleROITool.ts index 75f3fc9db..cd4c92b8d 100644 --- a/packages/tools/src/tools/annotation/RectangleROITool.ts +++ b/packages/tools/src/tools/annotation/RectangleROITool.ts @@ -355,7 +355,6 @@ class RectangleROITool extends AnnotationTool { return; } - annotation.highlighted = false; data.handles.activeHandleIndex = null; this._deactivateModify(element); diff --git a/packages/tools/src/tools/segmentation/CircleScissorsTool.ts b/packages/tools/src/tools/segmentation/CircleScissorsTool.ts index 53c576250..724895545 100644 --- a/packages/tools/src/tools/segmentation/CircleScissorsTool.ts +++ b/packages/tools/src/tools/segmentation/CircleScissorsTool.ts @@ -239,7 +239,6 @@ class CircleScissorsTool extends BaseTool { return; } - annotation.highlighted = false; data.handles.activeHandleIndex = null; this._deactivateDraw(element); diff --git a/packages/tools/src/tools/segmentation/RectangleScissorsTool.ts b/packages/tools/src/tools/segmentation/RectangleScissorsTool.ts index ba3f843d9..781e91d8d 100644 --- a/packages/tools/src/tools/segmentation/RectangleScissorsTool.ts +++ b/packages/tools/src/tools/segmentation/RectangleScissorsTool.ts @@ -276,7 +276,6 @@ class RectangleScissorsTool extends BaseTool { return; } - annotation.highlighted = false; data.handles.activeHandleIndex = null; this._deactivateDraw(element); diff --git a/packages/tools/src/tools/segmentation/SphereScissorsTool.ts b/packages/tools/src/tools/segmentation/SphereScissorsTool.ts index 904abb40a..1ef669753 100644 --- a/packages/tools/src/tools/segmentation/SphereScissorsTool.ts +++ b/packages/tools/src/tools/segmentation/SphereScissorsTool.ts @@ -239,7 +239,6 @@ class SphereScissorsTool extends BaseTool { if (newAnnotation && !hasMoved) { return; } - annotation.highlighted = false; data.handles.activeHandleIndex = null; diff --git a/packages/tools/test/LengthTool_test.js b/packages/tools/test/LengthTool_test.js index 9c3cc1491..f5776caef 100644 --- a/packages/tools/test/LengthTool_test.js +++ b/packages/tools/test/LengthTool_test.js @@ -245,7 +245,7 @@ describe('LengthTool:', () => { const lengthAnnotation = lengthAnnotations[0]; expect(lengthAnnotation.metadata.toolName).toBe(LengthTool.toolName); expect(lengthAnnotation.invalidated).toBe(false); - expect(lengthAnnotation.highlighted).toBe(false); + expect(lengthAnnotation.highlighted).toBe(true); const data = lengthAnnotation.data.cachedStats; const targets = Array.from(Object.keys(data)); @@ -359,7 +359,7 @@ describe('LengthTool:', () => { expect(lengthAnnotation.metadata.referencedImageId).toBe(imageId1); expect(lengthAnnotation.metadata.toolName).toBe(LengthTool.toolName); expect(lengthAnnotation.invalidated).toBe(false); - expect(lengthAnnotation.highlighted).toBe(false); + expect(lengthAnnotation.highlighted).toBe(true); const data = lengthAnnotation.data.cachedStats; const targets = Array.from(Object.keys(data)); @@ -500,7 +500,7 @@ describe('LengthTool:', () => { expect(lengthAnnotation.metadata.referencedImageId).toBe(imageId1); expect(lengthAnnotation.metadata.toolName).toBe(LengthTool.toolName); expect(lengthAnnotation.invalidated).toBe(false); - expect(lengthAnnotation.highlighted).toBe(false); + expect(lengthAnnotation.highlighted).toBe(true); const data = lengthAnnotation.data.cachedStats; const targets = Array.from(Object.keys(data)); @@ -1139,7 +1139,7 @@ describe('LengthTool:', () => { const lengthAnnotation = lengthAnnotations[0] expect(lengthAnnotation.metadata.toolName).toBe(LengthTool.toolName) expect(lengthAnnotation.invalidated).toBe(false) - expect(lengthAnnotation.highlighted).toBe(false) + expect(lengthAnnotation.highlighted).toBe(true) const data = lengthAnnotation.data.cachedStats const targets = Array.from(Object.keys(data)) diff --git a/packages/tools/test/cpu_LengthTool_test.js b/packages/tools/test/cpu_LengthTool_test.js index 3f1f30a1f..8aaeee992 100644 --- a/packages/tools/test/cpu_LengthTool_test.js +++ b/packages/tools/test/cpu_LengthTool_test.js @@ -250,7 +250,7 @@ describe('Length Tool (CPU):', () => { expect(lengthAnnotation.metadata.referencedImageId).toBe(imageId1); expect(lengthAnnotation.metadata.toolName).toBe(LengthTool.toolName); expect(lengthAnnotation.invalidated).toBe(false); - expect(lengthAnnotation.highlighted).toBe(false); + expect(lengthAnnotation.highlighted).toBe(true); const data = lengthAnnotation.data.cachedStats; const targets = Array.from(Object.keys(data)); @@ -391,7 +391,7 @@ describe('Length Tool (CPU):', () => { expect(lengthAnnotation.metadata.referencedImageId).toBe(imageId1); expect(lengthAnnotation.metadata.toolName).toBe(LengthTool.toolName); expect(lengthAnnotation.invalidated).toBe(false); - expect(lengthAnnotation.highlighted).toBe(false); + expect(lengthAnnotation.highlighted).toBe(true); const data = lengthAnnotation.data.cachedStats; const targets = Array.from(Object.keys(data)); From 5999830df83ac91252a1dd9ddf079f13167c769d Mon Sep 17 00:00:00 2001 From: ohif-bot Date: Wed, 23 Nov 2022 15:13:37 +0000 Subject: [PATCH 02/25] chore(release): publish [skip ci] - docs@0.7.8 - @cornerstonejs/tools@0.29.7 --- packages/docs/CHANGELOG.md | 8 ++++++++ packages/docs/package.json | 4 ++-- packages/tools/CHANGELOG.md | 11 +++++++++++ packages/tools/package.json | 2 +- 4 files changed, 22 insertions(+), 3 deletions(-) diff --git a/packages/docs/CHANGELOG.md b/packages/docs/CHANGELOG.md index cef9597bf..f02452622 100644 --- a/packages/docs/CHANGELOG.md +++ b/packages/docs/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.7.8](https://github.com/cornerstonejs/cornerstone3D-beta/compare/docs@0.7.7...docs@0.7.8) (2022-11-23) + +**Note:** Version bump only for package docs + + + + + ## [0.7.7](https://github.com/cornerstonejs/cornerstone3D-beta/compare/docs@0.7.6...docs@0.7.7) (2022-11-21) **Note:** Version bump only for package docs diff --git a/packages/docs/package.json b/packages/docs/package.json index ef586868f..e35ef64f7 100644 --- a/packages/docs/package.json +++ b/packages/docs/package.json @@ -1,6 +1,6 @@ { "name": "docs", - "version": "0.7.7", + "version": "0.7.8", "private": true, "repository": "https://github.com/cornerstonejs/cornerstone3D-beta", "scripts": { @@ -30,7 +30,7 @@ "dependencies": { "@cornerstonejs/core": "^0.21.4", "@cornerstonejs/streaming-image-volume-loader": "^0.6.5", - "@cornerstonejs/tools": "^0.29.6", + "@cornerstonejs/tools": "^0.29.7", "@docusaurus/core": "2.0.0-rc.1", "@docusaurus/module-type-aliases": "2.0.0-rc.1", "@docusaurus/preset-classic": "2.0.0-rc.1", diff --git a/packages/tools/CHANGELOG.md b/packages/tools/CHANGELOG.md index 1c3a1a50b..74e5bc4f7 100644 --- a/packages/tools/CHANGELOG.md +++ b/packages/tools/CHANGELOG.md @@ -3,6 +3,17 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.29.7](https://github.com/cornerstonejs/cornerstone3D-beta/compare/@cornerstonejs/tools@0.29.6...@cornerstonejs/tools@0.29.7) (2022-11-23) + + +### Bug Fixes + +* mouse-up should not unhighlight annotations ([#305](https://github.com/cornerstonejs/cornerstone3D-beta/issues/305)) ([0ca9653](https://github.com/cornerstonejs/cornerstone3D-beta/commit/0ca96533d253c35534c9820e4174b54270483d5e)) + + + + + ## [0.29.6](https://github.com/cornerstonejs/cornerstone3D-beta/compare/@cornerstonejs/tools@0.29.5...@cornerstonejs/tools@0.29.6) (2022-11-21) diff --git a/packages/tools/package.json b/packages/tools/package.json index c73df7491..b89d75011 100644 --- a/packages/tools/package.json +++ b/packages/tools/package.json @@ -1,6 +1,6 @@ { "name": "@cornerstonejs/tools", - "version": "0.29.6", + "version": "0.29.7", "description": "Cornerstone3D Tools", "main": "dist/umd/index.js", "types": "dist/esm/index.d.ts", From 5605a39b17749f4f1d0bf6f3ee6f5ee9be492be8 Mon Sep 17 00:00:00 2001 From: Alireza Date: Wed, 23 Nov 2022 10:39:37 -0500 Subject: [PATCH 03/25] fix: stack viewport flip scroll (#304) * fix: use focal point for pan cache for stack viewport * fix: pan dir with flip * fix pan values while flipped * update build * apply review comments * fix build --- common/reviews/api/core.api.md | 4 +- .../core/src/RenderingEngine/StackViewport.ts | 59 +++++++------- packages/core/src/RenderingEngine/Viewport.ts | 81 +++++++++---------- 3 files changed, 73 insertions(+), 71 deletions(-) diff --git a/common/reviews/api/core.api.md b/common/reviews/api/core.api.md index 673d2db45..560f54631 100644 --- a/common/reviews/api/core.api.md +++ b/common/reviews/api/core.api.md @@ -2020,8 +2020,8 @@ export class Viewport implements IViewport { _getEdges(bounds: Array): Array<[number[], number[]]>; // (undocumented) _getFocalPointForResetCamera(centeredFocalPoint: Point3, previousCamera: ICamera, { resetPan, resetToCenter }: { - resetPan: boolean; - resetToCenter: boolean; + resetPan?: boolean; + resetToCenter?: boolean; }): Point3; // (undocumented) getFrameOfReferenceUID: () => string; diff --git a/packages/core/src/RenderingEngine/StackViewport.ts b/packages/core/src/RenderingEngine/StackViewport.ts index f3158554c..a7d71912a 100644 --- a/packages/core/src/RenderingEngine/StackViewport.ts +++ b/packages/core/src/RenderingEngine/StackViewport.ts @@ -138,9 +138,8 @@ class StackViewport extends Viewport implements IStackViewport { // Helpers private _imageData: vtkImageDataType; - private cameraPosOnRender: Point3; + private cameraFocalPointOnRender: Point3; // we use focalPoint since flip manipulates the position and makes it useless to track private stackInvalidated = false; // if true -> new actor is forced to be created for the stack - private panCache: Point3; private voiApplied = false; private rotationCache = 0; private _publishCalibratedEvent = false; @@ -196,8 +195,7 @@ class StackViewport extends Viewport implements IStackViewport { this.imageIds = []; this.currentImageIdIndex = 0; this.targetImageIdIndex = 0; - this.panCache = [0, 0, 0]; - this.cameraPosOnRender = [0, 0, 0]; + this.cameraFocalPointOnRender = [0, 0, 0]; this.resetCamera(); this.initializeElementDisabledHandler(); @@ -1221,7 +1219,7 @@ class StackViewport extends Viewport implements IStackViewport { break; default: - console.debug('bit allocation not implemented'); + console.log('bit allocation not implemented'); } const scalarArray = vtkDataArray.newInstance({ @@ -1666,9 +1664,11 @@ class StackViewport extends Viewport implements IStackViewport { // it in the space 3) restore the pan, zoom props. const cameraProps = this.getCamera(); - this.panCache[0] = this.cameraPosOnRender[0] - cameraProps.position[0]; - this.panCache[1] = this.cameraPosOnRender[1] - cameraProps.position[1]; - this.panCache[2] = this.cameraPosOnRender[2] - cameraProps.position[2]; + const panCache = vec3.subtract( + vec3.create(), + this.cameraFocalPointOnRender, + cameraProps.focalPoint + ); // store rotation cache since reset camera will reset it const rotationCache = this.rotationCache; @@ -1680,8 +1680,15 @@ class StackViewport extends Viewport implements IStackViewport { // restore the rotation cache for the new slice this.setRotation(rotationCache, rotationCache); - const { position } = this.getCamera(); - this.cameraPosOnRender = position; + // set the flip back to the previous value since the restore camera props + // rely on the correct flip value + this.setCameraNoEvent({ + flipHorizontal: previousCameraProps.flipHorizontal, + flipVertical: previousCameraProps.flipVertical, + }); + + const { focalPoint } = this.getCamera(); + this.cameraFocalPointOnRender = focalPoint; // This is necessary to initialize the clipping range and it is not related // to our custom slabThickness. @@ -1690,7 +1697,11 @@ class StackViewport extends Viewport implements IStackViewport { // We shouldn't restore the focalPoint, position and parallelScale after reset // if it is the first render or we have completely re-created the vtkImageData - this._restoreCameraProps(cameraProps, previousCameraProps); + this._restoreCameraProps( + cameraProps, + previousCameraProps, + panCache as Point3 + ); // Restore rotation for the new slice of the image this.rotationCache = 0; @@ -1796,8 +1807,8 @@ class StackViewport extends Viewport implements IStackViewport { actor.getProperty().setRGBTransferFunction(0, cfun); // Saving position of camera on render, to cache the panning - const { position } = this.getCamera(); - this.cameraPosOnRender = position; + const { focalPoint } = this.getCamera(); + this.cameraFocalPointOnRender = focalPoint; this.stackInvalidated = false; if (this._publishCalibratedEvent) { @@ -1977,30 +1988,24 @@ class StackViewport extends Viewport implements IStackViewport { */ private _restoreCameraProps( { parallelScale: prevScale }: ICamera, - previousCamera: ICamera + previousCamera: ICamera, + panCache: Point3 ): void { const renderer = this.getRenderer(); // get the focalPoint and position after the reset const { position, focalPoint } = this.getCamera(); - const newPosition = [ - position[0] - this.panCache[0], - position[1] - this.panCache[1], - position[2] - this.panCache[2], - ]; - - const newFocal = [ - focalPoint[0] - this.panCache[0], - focalPoint[1] - this.panCache[1], - focalPoint[2] - this.panCache[2], - ]; + const newPosition = vec3.subtract(vec3.create(), position, panCache); + const newFocal = vec3.subtract(vec3.create(), focalPoint, panCache); // Restoring previous state x,y and scale, keeping the new z + // we need to break the flip operations since they also work on the + // camera position and focal point this.setCameraNoEvent({ parallelScale: prevScale, - position: newPosition, - focalPoint: newFocal, + position: newPosition as Point3, + focalPoint: newFocal as Point3, }); const camera = this.getCamera(); diff --git a/packages/core/src/RenderingEngine/Viewport.ts b/packages/core/src/RenderingEngine/Viewport.ts index 6e7913718..cd2d34e35 100644 --- a/packages/core/src/RenderingEngine/Viewport.ts +++ b/packages/core/src/RenderingEngine/Viewport.ts @@ -191,16 +191,12 @@ class Viewport implements IViewport { return; } - const { viewPlaneNormal, viewUp, focalPoint, position } = this.getCamera(); - - const viewRight = vec3.create(); - vec3.cross(viewRight, viewPlaneNormal, viewUp); - - let viewUpToSet = vec3.create(); - vec3.copy(viewUpToSet, viewUp); + const camera = this.getCamera(); + const { viewPlaneNormal, viewUp, focalPoint, position } = camera; - let viewPlaneNormalToSet = vec3.create(); - viewPlaneNormalToSet = vec3.negate(viewPlaneNormalToSet, viewPlaneNormal); + const viewRight = vec3.cross(vec3.create(), viewPlaneNormal, viewUp); + let viewUpToSet = vec3.copy(vec3.create(), viewUp); + const viewPlaneNormalToSet = vec3.negate(vec3.create(), viewPlaneNormal); // for both flip horizontal and vertical we need to move the camera to the // other side of the image @@ -208,25 +204,27 @@ class Viewport implements IViewport { // If the pan has been applied, we need to be able // apply the pan back - const resetFocalPoint = vec3.create(); const dimensions = imageData.getDimensions(); const middleIJK = dimensions.map((d) => Math.floor(d / 2)); const idx = [middleIJK[0], middleIJK[1], middleIJK[2]]; - imageData.indexToWorld(idx, resetFocalPoint); + const centeredFocalPoint = imageData.indexToWorld(idx, vec3.create()); - // what is the difference right now between the rested focal point and - // the current focal point - // Todo: this needs to be retrieved from the function that considers maintainFrame - // just now trying it on stack Viewport - const panDir = vec3.create(); - vec3.subtract(panDir, focalPoint, resetFocalPoint); + const resetFocalPoint = this._getFocalPointForResetCamera( + centeredFocalPoint as Point3, + camera, + { resetPan: true, resetToCenter: false } + ); + const panDir = vec3.subtract(vec3.create(), focalPoint, resetFocalPoint); const panValue = vec3.length(panDir); const getPanDir = (mirrorVec) => { - const panDirMirror = vec3.create(); - vec3.scale(panDirMirror, mirrorVec, 2 * vec3.dot(panDir, mirrorVec)); + const panDirMirror = vec3.scale( + vec3.create(), + mirrorVec, + 2 * vec3.dot(panDir, mirrorVec) + ); vec3.subtract(panDirMirror, panDirMirror, panDir); vec3.normalize(panDirMirror, panDirMirror); @@ -240,20 +238,22 @@ class Viewport implements IViewport { // we need to apply the pan value to the new focal point but in the direction // that is mirrored on the viewUp for the flip horizontal and // viewRight for the flip vertical - const newFocalPoint = vec3.create(); // mirror the pan direction based on the viewUp const panDirMirror = getPanDir(viewUpToSet); // move focal point from the resetFocalPoint to the newFocalPoint // based on the panDirMirror and panValue - vec3.scaleAndAdd(newFocalPoint, resetFocalPoint, panDirMirror, panValue); + const newFocalPoint = vec3.scaleAndAdd( + vec3.create(), + resetFocalPoint, + panDirMirror, + panValue + ); // move the camera position also the same way as the focal point - const newPosition = vec3.create(); - - vec3.scaleAndAdd( - newPosition, + const newPosition = vec3.scaleAndAdd( + vec3.create(), newFocalPoint, viewPlaneNormalToSet, distance @@ -276,14 +276,15 @@ class Viewport implements IViewport { // we need to apply the pan value to the new focal point but in the direction const panDirMirror = getPanDir(viewRight); - const newFocalPoint = vec3.create(); - - vec3.scaleAndAdd(newFocalPoint, resetFocalPoint, panDirMirror, panValue); - - const newPosition = vec3.create(); + const newFocalPoint = vec3.scaleAndAdd( + vec3.create(), + resetFocalPoint, + panDirMirror, + panValue + ); - vec3.scaleAndAdd( - newPosition, + const newPosition = vec3.scaleAndAdd( + vec3.create(), newFocalPoint, viewPlaneNormalToSet, distance @@ -1065,9 +1066,7 @@ class Viewport implements IViewport { const viewUpCorners = this._getCorners(bounds); const viewRightCorners = this._getCorners(bounds); - const viewRight = vec3.create(); - - vec3.cross(viewRight, viewUp, viewPlaneNormal); + const viewRight = vec3.cross(vec3.create(), viewUp, viewPlaneNormal); let transform = vtkMatrixBuilder .buildFromDegree() @@ -1131,7 +1130,7 @@ class Viewport implements IViewport { _getFocalPointForResetCamera( centeredFocalPoint: Point3, previousCamera: ICamera, - { resetPan, resetToCenter }: { resetPan: boolean; resetToCenter: boolean } + { resetPan = true, resetToCenter = true } ): Point3 { if (resetToCenter && resetPan) { return centeredFocalPoint; @@ -1152,9 +1151,8 @@ class Viewport implements IViewport { const oldFocalPoint = oldCamera.focalPoint; const oldViewPlaneNormal = oldCamera.viewPlaneNormal; - const vectorFromOldFocalPointToCenteredFocalPoint = vec3.create(); - vec3.subtract( - vectorFromOldFocalPointToCenteredFocalPoint, + const vectorFromOldFocalPointToCenteredFocalPoint = vec3.subtract( + vec3.create(), centeredFocalPoint, oldFocalPoint ); @@ -1164,9 +1162,8 @@ class Viewport implements IViewport { oldViewPlaneNormal ); - const newFocalPoint = vec3.create(); - vec3.scaleAndAdd( - newFocalPoint, + const newFocalPoint = vec3.scaleAndAdd( + vec3.create(), centeredFocalPoint, oldViewPlaneNormal, -1 * distanceFromOldFocalPointToCenteredFocalPoint From 049b292ba654ea2fedb7c21f3c6a11eaa3f2fc24 Mon Sep 17 00:00:00 2001 From: ohif-bot Date: Wed, 23 Nov 2022 15:44:17 +0000 Subject: [PATCH 04/25] chore(release): publish [skip ci] - @cornerstonejs/core@0.21.5 - docs@0.7.9 - @cornerstonejs/streaming-image-volume-loader@0.6.6 - @cornerstonejs/tools@0.29.8 --- packages/core/CHANGELOG.md | 11 +++++++++++ packages/core/package.json | 2 +- packages/docs/CHANGELOG.md | 8 ++++++++ packages/docs/package.json | 8 ++++---- packages/streaming-image-volume-loader/CHANGELOG.md | 8 ++++++++ packages/streaming-image-volume-loader/package.json | 4 ++-- packages/tools/CHANGELOG.md | 8 ++++++++ packages/tools/package.json | 4 ++-- 8 files changed, 44 insertions(+), 9 deletions(-) diff --git a/packages/core/CHANGELOG.md b/packages/core/CHANGELOG.md index 933bfb1fe..cd39e21b5 100644 --- a/packages/core/CHANGELOG.md +++ b/packages/core/CHANGELOG.md @@ -3,6 +3,17 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.21.5](https://github.com/cornerstonejs/cornerstone3D-beta/compare/@cornerstonejs/core@0.21.4...@cornerstonejs/core@0.21.5) (2022-11-23) + + +### Bug Fixes + +* stack viewport flip scroll ([#304](https://github.com/cornerstonejs/cornerstone3D-beta/issues/304)) ([5605a39](https://github.com/cornerstonejs/cornerstone3D-beta/commit/5605a39b17749f4f1d0bf6f3ee6f5ee9be492be8)) + + + + + ## [0.21.4](https://github.com/cornerstonejs/cornerstone3D-beta/compare/@cornerstonejs/core@0.21.3...@cornerstonejs/core@0.21.4) (2022-11-21) diff --git a/packages/core/package.json b/packages/core/package.json index f024930fe..d92f48947 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "@cornerstonejs/core", - "version": "0.21.4", + "version": "0.21.5", "description": "", "main": "dist/umd/index.js", "types": "dist/esm/index.d.ts", diff --git a/packages/docs/CHANGELOG.md b/packages/docs/CHANGELOG.md index f02452622..eec619fe5 100644 --- a/packages/docs/CHANGELOG.md +++ b/packages/docs/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.7.9](https://github.com/cornerstonejs/cornerstone3D-beta/compare/docs@0.7.8...docs@0.7.9) (2022-11-23) + +**Note:** Version bump only for package docs + + + + + ## [0.7.8](https://github.com/cornerstonejs/cornerstone3D-beta/compare/docs@0.7.7...docs@0.7.8) (2022-11-23) **Note:** Version bump only for package docs diff --git a/packages/docs/package.json b/packages/docs/package.json index e35ef64f7..733c4472d 100644 --- a/packages/docs/package.json +++ b/packages/docs/package.json @@ -1,6 +1,6 @@ { "name": "docs", - "version": "0.7.8", + "version": "0.7.9", "private": true, "repository": "https://github.com/cornerstonejs/cornerstone3D-beta", "scripts": { @@ -28,9 +28,9 @@ "write-heading-ids": "docusaurus write-heading-ids" }, "dependencies": { - "@cornerstonejs/core": "^0.21.4", - "@cornerstonejs/streaming-image-volume-loader": "^0.6.5", - "@cornerstonejs/tools": "^0.29.7", + "@cornerstonejs/core": "^0.21.5", + "@cornerstonejs/streaming-image-volume-loader": "^0.6.6", + "@cornerstonejs/tools": "^0.29.8", "@docusaurus/core": "2.0.0-rc.1", "@docusaurus/module-type-aliases": "2.0.0-rc.1", "@docusaurus/preset-classic": "2.0.0-rc.1", diff --git a/packages/streaming-image-volume-loader/CHANGELOG.md b/packages/streaming-image-volume-loader/CHANGELOG.md index e86c2d7dc..aa1e4c31d 100644 --- a/packages/streaming-image-volume-loader/CHANGELOG.md +++ b/packages/streaming-image-volume-loader/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.6.6](https://github.com/cornerstonejs/cornerstone3D-beta/compare/@cornerstonejs/streaming-image-volume-loader@0.6.5...@cornerstonejs/streaming-image-volume-loader@0.6.6) (2022-11-23) + +**Note:** Version bump only for package @cornerstonejs/streaming-image-volume-loader + + + + + ## [0.6.5](https://github.com/cornerstonejs/cornerstone3D-beta/compare/@cornerstonejs/streaming-image-volume-loader@0.6.4...@cornerstonejs/streaming-image-volume-loader@0.6.5) (2022-11-21) **Note:** Version bump only for package @cornerstonejs/streaming-image-volume-loader diff --git a/packages/streaming-image-volume-loader/package.json b/packages/streaming-image-volume-loader/package.json index 335ffe25a..a72eddb11 100644 --- a/packages/streaming-image-volume-loader/package.json +++ b/packages/streaming-image-volume-loader/package.json @@ -1,6 +1,6 @@ { "name": "@cornerstonejs/streaming-image-volume-loader", - "version": "0.6.5", + "version": "0.6.6", "description": "", "main": "dist/umd/index.js", "types": "dist/esm/index.d.ts", @@ -26,7 +26,7 @@ "webpack:watch": "webpack --mode development --progress --watch --config ./.webpack/webpack.dev.js" }, "dependencies": { - "@cornerstonejs/core": "^0.21.4", + "@cornerstonejs/core": "^0.21.5", "cornerstone-wado-image-loader": "^4.2.1" }, "peerDependencies": { diff --git a/packages/tools/CHANGELOG.md b/packages/tools/CHANGELOG.md index 74e5bc4f7..637ca58ca 100644 --- a/packages/tools/CHANGELOG.md +++ b/packages/tools/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.29.8](https://github.com/cornerstonejs/cornerstone3D-beta/compare/@cornerstonejs/tools@0.29.7...@cornerstonejs/tools@0.29.8) (2022-11-23) + +**Note:** Version bump only for package @cornerstonejs/tools + + + + + ## [0.29.7](https://github.com/cornerstonejs/cornerstone3D-beta/compare/@cornerstonejs/tools@0.29.6...@cornerstonejs/tools@0.29.7) (2022-11-23) diff --git a/packages/tools/package.json b/packages/tools/package.json index b89d75011..9e40ef216 100644 --- a/packages/tools/package.json +++ b/packages/tools/package.json @@ -1,6 +1,6 @@ { "name": "@cornerstonejs/tools", - "version": "0.29.7", + "version": "0.29.8", "description": "Cornerstone3D Tools", "main": "dist/umd/index.js", "types": "dist/esm/index.d.ts", @@ -26,7 +26,7 @@ "webpack:watch": "webpack --mode development --progress --watch --config ./.webpack/webpack.dev.js" }, "dependencies": { - "@cornerstonejs/core": "^0.21.4", + "@cornerstonejs/core": "^0.21.5", "lodash.clonedeep": "4.5.0", "lodash.get": "^4.4.2" }, From 3303246836c81efb51e5d5e70c1a8801fbcb019a Mon Sep 17 00:00:00 2001 From: Niclas do <32524613+doepnern@users.noreply.github.com> Date: Wed, 23 Nov 2022 16:46:46 +0100 Subject: [PATCH 05/25] feat: add referenceCursors tool (#275) * added basic cursorCrosshairSync tool with example, TODO: for now cursorSync is displayed regardless of distance, create configurable distance and also sync the position of all viewports over which the mouse is not to scroll to a slice that is close to the currentMousePosition in 3d space * addde stack syncing for StackViewport and syncing for volumeViewport on imageChange events, added configuration for max display distance * refactored tool functions * added comment to possible bug * added configuration options to example * changed look of crosshair to 4 lines with central space * undid local tsconfig change * undid yarn.lock changes * added tool to example-info.json * removed from example-runner because it broke build * readded example and fixed typo * readded example-info and changed example to trigger rebuild * added cleanup for mouseoverElement when tool is disabled * added cleanup when tool gets disabled, this does not get called when toolGroup gets destroyed, might cause remaining listeners * applied naming changes, reworked adding annotation logic * removed event listeners and moved logic to check for stack scrolling into rendering logic * added planeDistanceToPoint to planar utilities * added getClosestStackImageIndexForPoint * rewrote logic to use onCameraModified * updated example-info * fixed bug with 0 being falsey * added logic to remove cursor if wanted * modified toolGroup so that setting a tool active only changes the cursor to default if there is no primary mouse cursor * fixed bug not updating disable cursor * fixed missing parentheses from merge * readded scrollWheel scrolling and api changes * fixed typos --- common/reviews/api/core.api.md | 10 +- common/reviews/api/tools.api.md | 58 +++ .../getClosestStackImageIndexForPoint.ts | 116 +++++ packages/core/src/utilities/index.ts | 2 + packages/core/src/utilities/planar.ts | 27 +- .../tools/examples/referenceCursors/index.ts | 300 +++++++++++ packages/tools/src/index.ts | 2 + .../src/store/ToolGroupManager/ToolGroup.ts | 9 +- packages/tools/src/tools/ReferenceCursors.ts | 471 ++++++++++++++++++ packages/tools/src/tools/index.ts | 2 + .../src/types/ToolSpecificAnnotationTypes.ts | 8 + utils/ExampleRunner/example-info.json | 4 + 12 files changed, 1004 insertions(+), 5 deletions(-) create mode 100644 packages/core/src/utilities/getClosestStackImageIndexForPoint.ts create mode 100644 packages/tools/examples/referenceCursors/index.ts create mode 100644 packages/tools/src/tools/ReferenceCursors.ts diff --git a/common/reviews/api/core.api.md b/common/reviews/api/core.api.md index 560f54631..ebda708ed 100644 --- a/common/reviews/api/core.api.md +++ b/common/reviews/api/core.api.md @@ -545,6 +545,9 @@ type FlipDirection = { // @public (undocumented) function getClosestImageId(imageVolume: IImageVolume, worldPos: Point3, viewPlaneNormal: Point3, viewUp: Point3): string; +// @public (undocumented) +function getClosestStackImageIndexForPoint(point: Point3, viewport: IStackViewport): number | null; + // @public (undocumented) export function getEnabledElement(element: HTMLDivElement | undefined): IEnabledElement | undefined; @@ -1521,13 +1524,17 @@ declare namespace planar { export { linePlaneIntersection, planeEquation, - threePlaneIntersection + threePlaneIntersection, + planeDistanceToPoint } } // @public (undocumented) type Plane = [number, number, number, number]; +// @public (undocumented) +function planeDistanceToPoint(plane: Plane, point: Point3, signed?: boolean): number; + // @public (undocumented) function planeEquation(normal: Point3, point: Point3 | vec3): Plane; @@ -1965,6 +1972,7 @@ declare namespace utilities { getImageSliceDataForVolumeViewport, isImageActor, getViewportsWithImageURI, + getClosestStackImageIndexForPoint, calculateViewportsSpatialRegistration, spatialRegistrationMetadataProvider, getViewportImageCornersInWorld diff --git a/common/reviews/api/tools.api.md b/common/reviews/api/tools.api.md index 5aabc1f10..b89234a61 100644 --- a/common/reviews/api/tools.api.md +++ b/common/reviews/api/tools.api.md @@ -3591,6 +3591,63 @@ export class RectangleScissorsTool extends BaseTool { static toolName: any; } +// @public (undocumented) +interface ReferenceCursor extends Annotation { + // (undocumented) + data: { + handles: { + points: [Types_2.Point3]; + }; + }; +} + +// @public (undocumented) +export class ReferenceCursors extends AnnotationDisplayTool { + constructor(toolProps?: PublicToolProps, defaultToolProps?: ToolProps); + // (undocumented) + _addAnnotation(element: HTMLDivElement, annotation: Annotation): string | null; + // (undocumented) + createInitialAnnotation: (worldPos: Types_2.Point3, element: HTMLDivElement) => void; + // (undocumented) + _currentCanvasPosition: null | Types_2.Point2; + // (undocumented) + _currentCursorWorldPosition: null | Types_2.Point3; + // (undocumented) + _disableCursorEnabled: boolean; + // (undocumented) + _elementWithCursor: null | HTMLDivElement; + // (undocumented) + filterInteractableAnnotationsForElement(element: HTMLDivElement, annotations: Annotations): Annotations; + // (undocumented) + getActiveAnnotation(element: HTMLDivElement): null | Annotation; + // (undocumented) + isDrawing: boolean; + // (undocumented) + isHandleOutsideImage: boolean; + // (undocumented) + mouseDragCallback: any; + // (undocumented) + mouseMoveCallback: (evt: EventTypes_2.MouseMoveEventType) => boolean; + // (undocumented) + onCameraModified: (evt: any) => void; + // (undocumented) + onSetToolActive(): void; + // (undocumented) + onSetToolDisabled(): void; + // (undocumented) + renderAnnotation: (enabledElement: Types_2.IEnabledElement, svgDrawingHelper: SVGDrawingHelper) => boolean; + // (undocumented) + _throttledCalculateCachedStats: any; + // (undocumented) + static toolName: any; + // (undocumented) + touchDragCallback: any; + // (undocumented) + updateAnnotationPosition(element: HTMLDivElement, annotation: Annotation): void; + // (undocumented) + updateViewportImage(viewport: Types_2.IStackViewport | Types_2.IVolumeViewport): void; +} + // @public (undocumented) interface ReferenceLineAnnotation extends Annotation { // (undocumented) @@ -4336,6 +4393,7 @@ declare namespace ToolSpecificAnnotationTypes { PlanarFreehandROIAnnotation, ArrowAnnotation, AngleAnnotation, + ReferenceCursor, ReferenceLineAnnotation } } diff --git a/packages/core/src/utilities/getClosestStackImageIndexForPoint.ts b/packages/core/src/utilities/getClosestStackImageIndexForPoint.ts new file mode 100644 index 000000000..ada80b1f9 --- /dev/null +++ b/packages/core/src/utilities/getClosestStackImageIndexForPoint.ts @@ -0,0 +1,116 @@ +import { vec3 } from 'gl-matrix'; +import { planar } from '.'; +import { metaData } from '..'; +import { IStackViewport, Point3 } from '../types'; + +/** + * Given a point in 3D space and a viewport it returns the index of the closest imageId, it assumes that stack images are sorted according to their sliceLocation + * @param point - [A, B, C] coordinates of the point in 3D space + * @param viewport - The StackViewport to search for the closest imageId + * + * @returns The imageId index of the closest imageId or null if no imageId is found + */ +export default function getClosestStackImageIndexForPoint( + point: Point3, + viewport: IStackViewport +): number | null { + const minimalDistance = calculateMinimalDistanceForStackViewport( + point, + viewport + ); + return minimalDistance ? minimalDistance.index : null; +} + +//assumes that imageIds are sorted by slice location +export function calculateMinimalDistanceForStackViewport( + point: Point3, + viewport: IStackViewport +): { distance: number; index: number } | null { + const imageIds = viewport.getImageIds(); + const currentImageIdIndex = viewport.getCurrentImageIdIndex(); + + if (imageIds.length === 0) return null; + + const getDistance = (imageId: string): null | number => { + const planeMetadata = getPlaneMetadata(imageId); + if (!planeMetadata) return null; + const plane = planar.planeEquation( + planeMetadata.planeNormal, + planeMetadata.imagePositionPatient + ); + const distance = planar.planeDistanceToPoint(plane, point); + return distance; + }; + + const closestStack = { + distance: getDistance(imageIds[currentImageIdIndex]) ?? Infinity, + index: currentImageIdIndex, + }; + + //check higher indices + const higherImageIds = imageIds.slice(currentImageIdIndex + 1); + + for (let i = 0; i < higherImageIds.length; i++) { + const id = higherImageIds[i]; + const distance = getDistance(id); + if (distance === null) continue; + if (distance <= closestStack.distance) { + closestStack.distance = distance; + closestStack.index = i + currentImageIdIndex + 1; + } else break; + } + //check lower indices + const lowerImageIds = imageIds.slice(0, currentImageIdIndex); + for (let i = lowerImageIds.length - 1; i >= 0; i--) { + const id = lowerImageIds[i]; + const distance = getDistance(id); + if (distance === null || distance === closestStack.distance) continue; + if (distance < closestStack.distance) { + closestStack.distance = distance; + closestStack.index = i; + } else break; + } + return closestStack.distance === Infinity ? null : closestStack; +} + +function getPlaneMetadata(imageId: string): null | { + rowCosines: Point3; + columnCosines: Point3; + imagePositionPatient: Point3; + planeNormal: Point3; +} { + const targetImagePlane = metaData.get('imagePlaneModule', imageId); + + if ( + !targetImagePlane || + !( + targetImagePlane.rowCosines instanceof Array && + targetImagePlane.rowCosines.length === 3 + ) || + !( + targetImagePlane.columnCosines instanceof Array && + targetImagePlane.columnCosines.length === 3 + ) || + !( + targetImagePlane.imagePositionPatient instanceof Array && + targetImagePlane.imagePositionPatient.length === 3 + ) + ) { + return null; + } + const { + rowCosines, + columnCosines, + imagePositionPatient, + }: { + rowCosines: Point3; + columnCosines: Point3; + imagePositionPatient: Point3; + } = targetImagePlane; + + const rowVec = vec3.set(vec3.create(), ...rowCosines); + const colVec = vec3.set(vec3.create(), ...columnCosines); + const planeNormal = vec3.cross(vec3.create(), rowVec, colVec) as Point3; + + return { rowCosines, columnCosines, imagePositionPatient, planeNormal }; +} diff --git a/packages/core/src/utilities/index.ts b/packages/core/src/utilities/index.ts index 2bf011903..be6aa7773 100644 --- a/packages/core/src/utilities/index.ts +++ b/packages/core/src/utilities/index.ts @@ -27,6 +27,7 @@ import snapFocalPointToSlice from './snapFocalPointToSlice'; import getImageSliceDataForVolumeViewport from './getImageSliceDataForVolumeViewport'; import isImageActor from './isImageActor'; import getViewportsWithImageURI from './getViewportsWithImageURI'; +import getClosestStackImageIndexForPoint from './getClosestStackImageIndexForPoint'; import calculateViewportsSpatialRegistration from './calculateViewportsSpatialRegistration'; import spatialRegistrationMetadataProvider from './spatialRegistrationMetadataProvider'; import getViewportImageCornersInWorld from './getViewportImageCornersInWorld'; @@ -67,6 +68,7 @@ export { getImageSliceDataForVolumeViewport, isImageActor, getViewportsWithImageURI, + getClosestStackImageIndexForPoint, calculateViewportsSpatialRegistration, spatialRegistrationMetadataProvider, getViewportImageCornersInWorld, diff --git a/packages/core/src/utilities/planar.ts b/packages/core/src/utilities/planar.ts index dac8513b3..d441c83c6 100644 --- a/packages/core/src/utilities/planar.ts +++ b/packages/core/src/utilities/planar.ts @@ -63,4 +63,29 @@ function threePlaneIntersection( return [x, y, z]; } -export { linePlaneIntersection, planeEquation, threePlaneIntersection }; +/** + * Computes the distance of a point in 3D space to a plane + * @param plane - [A, B, C, D] of plane equation A*X + B*Y + C*Z = D + * @param point - [A, B, C] the plane in World coordinate + * @param signed - if true, the distance is signed + * @returns - the distance of the point to the plane + * */ +function planeDistanceToPoint( + plane: Plane, + point: Point3, + signed = false +): number { + const [A, B, C, D] = plane; + const [x, y, z] = point; + const numerator = A * x + B * y + C * z - D; + const distance = Math.abs(numerator) / Math.sqrt(A * A + B * B + C * C); + const sign = signed ? Math.sign(numerator) : 1; + return sign * distance; +} + +export { + linePlaneIntersection, + planeEquation, + threePlaneIntersection, + planeDistanceToPoint, +}; diff --git a/packages/tools/examples/referenceCursors/index.ts b/packages/tools/examples/referenceCursors/index.ts new file mode 100644 index 000000000..e6e696e48 --- /dev/null +++ b/packages/tools/examples/referenceCursors/index.ts @@ -0,0 +1,300 @@ +import { + RenderingEngine, + Types, + volumeLoader, + Enums, +} from '@cornerstonejs/core'; +import { + initDemo, + createImageIdsAndCacheMetaData, + setTitleAndDescription, + addDropdownToToolbar, + addSliderToToolbar, +} from '../../../../utils/demo/helpers'; +import * as cornerstoneTools from '@cornerstonejs/tools'; + +// This is for debugging purposes +console.warn( + 'Click on index.ts to open source code for this example --------->' +); + +const { + ToolGroupManager, + StackScrollMouseWheelTool, + ReferenceCursors, + PanTool, + ZoomTool, + Enums: csToolsEnums, +} = cornerstoneTools; + +const { ViewportType } = Enums; + +// Define a unique id for the volume +const volumeName = 'CT_VOLUME_ID'; // Id of the volume less loader prefix +const volumeLoaderScheme = 'cornerstoneStreamingImageVolume'; // Loader id which defines which volume loader to use +const volumeId = `${volumeLoaderScheme}:${volumeName}`; // VolumeId with loader id + volume id + +const initialDisableCursor = false; + +// ======== Set up page ======== // +setTitleAndDescription( + 'Cursor corsshair syncing example', + 'This example shows how to sync the crosshair cursors between 3 viewports (2 Stack viewports and 1 Volume viewport with a slightly different orientation). To disable orther cursors, set disableCursor to on and then disable and reactivate the tool.' +); + +const size = '500px'; +const content = document.getElementById('content'); +const viewportGrid = document.createElement('div'); + +viewportGrid.style.display = 'flex'; +viewportGrid.style.display = 'flex'; +viewportGrid.style.flexDirection = 'row'; + +const element1 = document.createElement('div'); +const element2 = document.createElement('div'); +const element3 = document.createElement('div'); +element1.style.width = size; +element1.style.height = size; +element1.oncontextmenu = (e) => e.preventDefault(); +element2.style.width = size; +element2.style.height = size; +element2.oncontextmenu = (e) => e.preventDefault(); +element3.style.height = size; +element3.style.width = size; +element3.oncontextmenu = (e) => e.preventDefault(); + +viewportGrid.appendChild(element1); +viewportGrid.appendChild(element2); +viewportGrid.appendChild(element3); + +content.appendChild(viewportGrid); + +const toolGroupId = 'STACK_TOOL_GROUP_ID'; + +const instructions = document.createElement('p'); +instructions.innerText = + 'Simply move the mouse over the viewports to see the correlating positions in the other viewports'; + +content.append(instructions); + +addDropdownToToolbar({ + options: { + values: ['positionSync on', 'positionSync off'], + defaultValue: 'positionSync on', + }, + onSelectedValueChange: (newPositionSync) => { + const toolGroup = ToolGroupManager.getToolGroup(toolGroupId); + if (toolGroup) { + toolGroup.setToolConfiguration(ReferenceCursors.toolName, { + positionSync: newPositionSync === 'positionSync on', + }); + } + }, +}); + +addDropdownToToolbar({ + options: { + values: ['disableCursor on', 'disableCursor off'], + defaultValue: initialDisableCursor + ? 'disableCursor on' + : 'disableCursor off', + }, + onSelectedValueChange: (newDisableCursor) => { + const toolGroup = ToolGroupManager.getToolGroup(toolGroupId); + if (toolGroup) { + toolGroup.setToolConfiguration(ReferenceCursors.toolName, { + disableCursor: newDisableCursor === 'disableCursor on', + }); + } + }, +}); + +addDropdownToToolbar({ + options: { + values: ['tool enabled', 'tool disabled', 'tool passive', 'tool active'], + defaultValue: 'tool active', + }, + onSelectedValueChange: (newState) => { + const toolGroup = ToolGroupManager.getToolGroup(toolGroupId); + if (toolGroup) { + switch (newState) { + case 'tool enabled': + toolGroup.setToolEnabled(ReferenceCursors.toolName); + break; + case 'tool disabled': + toolGroup.setToolDisabled(ReferenceCursors.toolName); + break; + case 'tool passive': + toolGroup.setToolPassive(ReferenceCursors.toolName); + break; + case 'tool active': + toolGroup.setToolActive(ReferenceCursors.toolName); + break; + default: + throw new Error('unhandled selector value'); + } + } + }, +}); + +addSliderToToolbar({ + title: ' displayThreshold: 5 ', + range: [0, 100], + defaultValue: 5, + updateLabelOnChange(value, label) { + label.innerText = ` displayThreshold: ${value} `; + }, + onSelectedValueChange: (newDisplayThreshold) => { + const toolGroup = ToolGroupManager.getToolGroup(toolGroupId); + if (toolGroup) { + toolGroup.setToolConfiguration(ReferenceCursors.toolName, { + displayThreshold: newDisplayThreshold, + }); + } + }, +}); +// ============================= // + +/** + * Runs the demo + */ +async function run() { + // Init Cornerstone and related libraries + await initDemo(); + + // Add tools to Cornerstone3D + cornerstoneTools.addTool(ReferenceCursors); + cornerstoneTools.addTool(StackScrollMouseWheelTool); + cornerstoneTools.addTool(PanTool); + cornerstoneTools.addTool(ZoomTool); + + // Define a tool group, which defines how mouse events map to tool commands for + // Any viewport using the group + const toolGroup = ToolGroupManager.createToolGroup(toolGroupId); + + // Add the tools to the tool group and specify which volume they are pointing at + toolGroup.addTool(ReferenceCursors.toolName); + toolGroup.addTool(StackScrollMouseWheelTool.toolName); + toolGroup.addTool(PanTool.toolName); + toolGroup.addTool(ZoomTool.toolName); + + toolGroup?.setToolConfiguration(ReferenceCursors.toolName, { + positionSync: true, + disableCursor: initialDisableCursor, + }); + + // Get Cornerstone imageIds and fetch metadata into RAM + const volumeImageIds = await createImageIdsAndCacheMetaData({ + StudyInstanceUID: + '1.3.6.1.4.1.14519.5.2.1.7009.2403.334240657131972136850343327463', + SeriesInstanceUID: + '1.3.6.1.4.1.14519.5.2.1.7009.2403.226151125820845824875394858561', + wadoRsRoot: 'https://d3t6nz73ql33tx.cloudfront.net/dicomweb', + type: 'VOLUME', + }); + + const stackImageIds = await createImageIdsAndCacheMetaData({ + StudyInstanceUID: + '1.3.6.1.4.1.14519.5.2.1.7009.2403.334240657131972136850343327463', + SeriesInstanceUID: + '1.3.6.1.4.1.14519.5.2.1.7009.2403.226151125820845824875394858561', + wadoRsRoot: 'https://d3t6nz73ql33tx.cloudfront.net/dicomweb', + type: 'STACK', + }); + + const smallVolumeImageIds = [volumeImageIds[42], volumeImageIds[43]]; // Small bit of the body + const smallStackImageIds = [stackImageIds[42], stackImageIds[43]]; // Small bit of the body + + // Instantiate a rendering engine + const renderingEngineId = 'myRenderingEngine'; + const renderingEngine = new RenderingEngine(renderingEngineId); + + // Create the viewports + const viewportIds = ['CT_AXIAL_VOLUME', 'CT_AXIAL_STACK', 'CT_AXIAL_STACK2']; + + const viewportInputArray = [ + { + viewportId: viewportIds[0], + type: ViewportType.ORTHOGRAPHIC, + element: element1, + defaultOptions: { + orientation: Enums.OrientationAxis.AXIAL, + background: [0.2, 0, 0.2], + }, + }, + { + viewportId: viewportIds[1], + type: ViewportType.STACK, + element: element2, + defaultOptions: { + background: [0.2, 0, 0.2], + }, + }, + { + viewportId: viewportIds[2], + type: ViewportType.STACK, + element: element3, + defaultOptions: { + background: [0.2, 0, 0.2], + }, + }, + ]; + + renderingEngine.setViewports(viewportInputArray); + + // Set the tool group on the viewports + viewportIds.forEach((viewportId) => + toolGroup.addViewport(viewportId, renderingEngineId) + ); + + // Define a volume in memory + const volume = await volumeLoader.createAndCacheVolume(volumeId, { + imageIds: smallVolumeImageIds, + }); + + const volumeViewport = ( + renderingEngine.getViewport(viewportIds[0]) + ); + const stackViewport = ( + renderingEngine.getViewport(viewportIds[1]) + ); + + const stackViewport2 = ( + renderingEngine.getViewport(viewportIds[2]) + ); + + // Set the stack on the stackViewport + stackViewport.setStack(smallStackImageIds); + stackViewport2.setStack(smallStackImageIds); + + // Set the volume to load + volume.load(); + + // Set the volume on the viewport + volumeViewport.setVolumes([{ volumeId }]).then(() => { + volumeViewport.setCamera({ + viewPlaneNormal: [0, 0.1, -1], + viewUp: [0, 1, 0], + }); + }); + + // As the Stack Scroll mouse wheel is a tool using the `mouseWheelCallback` + // hook instead of mouse buttons, it does not need to assign any mouse button. + toolGroup.setToolActive(StackScrollMouseWheelTool.toolName); + + toolGroup.setToolActive(PanTool.toolName, { + bindings: [{ mouseButton: csToolsEnums.MouseBindings.Primary }], + }); + toolGroup.setToolActive(ZoomTool.toolName, { + bindings: [{ mouseButton: csToolsEnums.MouseBindings.Secondary }], + }); + + // Set the initial state of the tools, here we set one tool active on left click. + // This means left click will draw that tool. + toolGroup.setToolActive(ReferenceCursors.toolName); + + // Render the image + renderingEngine.renderViewports(viewportIds); +} + +run(); diff --git a/packages/tools/src/index.ts b/packages/tools/src/index.ts index eb7f8a840..5a85179e2 100644 --- a/packages/tools/src/index.ts +++ b/packages/tools/src/index.ts @@ -50,6 +50,7 @@ import { BrushTool, AngleTool, MagnifyTool, + ReferenceCursors, ReferenceLines, } from './tools'; @@ -87,6 +88,7 @@ export { ArrowAnnotateTool, AngleTool, MagnifyTool, + ReferenceCursors, ReferenceLines, // Segmentation Display SegmentationDisplayTool, diff --git a/packages/tools/src/store/ToolGroupManager/ToolGroup.ts b/packages/tools/src/store/ToolGroupManager/ToolGroup.ts index ec2c880b1..2cf662fa5 100644 --- a/packages/tools/src/store/ToolGroupManager/ToolGroup.ts +++ b/packages/tools/src/store/ToolGroupManager/ToolGroup.ts @@ -273,9 +273,12 @@ export default class ToolGroup implements IToolGroup { if (this._hasMousePrimaryButtonBinding(toolBindingsOptions) && useCursor) { this.setViewportsCursorByToolName(toolName); } else { - // reset to default cursor - const cursor = MouseCursor.getDefinedCursor('default'); - this._setCursorForViewports(cursor); + // reset to default cursor only if there is no other tool with primary binding + const activeToolName = this.getActivePrimaryMouseButtonTool(); + if (!activeToolName && useCursor) { + const cursor = MouseCursor.getDefinedCursor('default'); + this._setCursorForViewports(cursor); + } } if (typeof this._toolInstances[toolName].onSetToolActive === 'function') { diff --git a/packages/tools/src/tools/ReferenceCursors.ts b/packages/tools/src/tools/ReferenceCursors.ts new file mode 100644 index 000000000..3911343b6 --- /dev/null +++ b/packages/tools/src/tools/ReferenceCursors.ts @@ -0,0 +1,471 @@ +import { + getEnabledElement, + StackViewport, + VolumeViewport, + utilities, + getEnabledElementByIds, +} from '@cornerstonejs/core'; +import type { Types } from '@cornerstonejs/core'; +import { + addAnnotation, + getAnnotations, +} from '../stateManagement/annotation/annotationState'; +import { isAnnotationVisible } from '../stateManagement/annotation/annotationVisibility'; +import { drawLine } from '../drawingSvg'; +import { getViewportIdsWithToolToRender } from '../utilities/viewportFilters'; +import { + EventTypes, + PublicToolProps, + ToolProps, + SVGDrawingHelper, + Annotation, + Annotations, +} from '../types'; +import { ReferenceCursor } from '../types/ToolSpecificAnnotationTypes'; + +import triggerAnnotationRenderForViewportIds from '../utilities/triggerAnnotationRenderForViewportIds'; +import { StyleSpecifier } from '../types/AnnotationStyle'; +import { vec3 } from 'gl-matrix'; +import AnnotationDisplayTool from './base/AnnotationDisplayTool'; +import vtkMath from '@kitware/vtk.js/Common/Core/Math'; +import { + hideElementCursor, + resetElementCursor, +} from '../cursors/elementCursor'; +import { getToolGroup } from '../store/ToolGroupManager'; + +/** + * ReferenceCursors is a tool that will show your cursors position in all other elements in the toolGroup if they have a matching FrameOfReference relative to its position in world space. + * Also when positionSync is enabled, it will try to sync viewports so that the cursor can be displayed in the correct position in all viewports. + * + * Configuration: + * - positionSync: boolean, if true, it will try to sync viewports so that the cursor can be displayed in the correct position in all viewports. + * - disableCursor: boolean, if true, it will hide the cursor in all viewports. You need to disable and reactivate the tool for this to apply. + * - displayThreshold: number, if the distance of the cursor in a viewport is bigger than this threshold the cursor will not be displayed. + * + * Only uses Active and Disabled state + */ +class ReferenceCursors extends AnnotationDisplayTool { + static toolName; + touchDragCallback: any; + mouseDragCallback: any; + _throttledCalculateCachedStats: any; + isDrawing = false; + isHandleOutsideImage = false; + _elementWithCursor: null | HTMLDivElement = null; + _currentCursorWorldPosition: null | Types.Point3 = null; + _currentCanvasPosition: null | Types.Point2 = null; + //need to keep track if this was enabled when tool was enabled because we need to know if we should reset cursors + _disableCursorEnabled = false; + + constructor( + toolProps: PublicToolProps = {}, + defaultToolProps: ToolProps = { + supportedInteractionTypes: ['Mouse', 'Touch'], + configuration: { + shadow: true, + preventHandleOutsideImage: false, + displayThreshold: 5, + positionSync: true, + disableCursor: false, + }, + } + ) { + super(toolProps, defaultToolProps); + this._disableCursorEnabled = this.configuration.disableCursor; + } + + /** + * Overwritten mouseMoveCallback since we want to keep track of the current mouse position and redraw on mouseMove + * @virtual Event handler for Cornerstone MOUSE_MOVE event. + * + * + * @param evt - The normalized mouse event + * @param filteredAnnotations - The annotations to check for hover interactions + * @returns True if the annotation needs to be re-drawn by the annotationRenderingEngine. + */ + mouseMoveCallback = (evt: EventTypes.MouseMoveEventType): boolean => { + const { detail } = evt; + const { element, currentPoints } = detail; + + //save current positions and current element the curser is hovering over + this._currentCursorWorldPosition = currentPoints.world; + this._currentCanvasPosition = currentPoints.canvas; + this._elementWithCursor = element; + + const annotation = this.getActiveAnnotation(element); + if (annotation === null) { + this.createInitialAnnotation(currentPoints.world, element); + return false; + } + this.updateAnnotationPosition(element, annotation); + return false; + }; + + onSetToolActive(): void { + this._disableCursorEnabled = this.configuration.disableCursor; + if (!this._disableCursorEnabled) return; + const viewportIds = getToolGroup(this.toolGroupId).viewportsInfo; + if (!viewportIds) return; + const enabledElements = viewportIds.map((e) => + getEnabledElementByIds(e.viewportId, e.renderingEngineId) + ); + + enabledElements.forEach((element) => { + if (element) hideElementCursor(element.viewport.element); + }); + } + onSetToolDisabled(): void { + if (!this._disableCursorEnabled) return; + const viewportIds = getToolGroup(this.toolGroupId).viewportsInfo; + if (!viewportIds) return; + const enabledElements = viewportIds.map((e) => + getEnabledElementByIds(e.viewportId, e.renderingEngineId) + ); + enabledElements.forEach((element) => { + if (element) resetElementCursor(element.viewport.element); + }); + } + + createInitialAnnotation = ( + worldPos: Types.Point3, + element: HTMLDivElement + ): void => { + const enabledElement = getEnabledElement(element); + if (!enabledElement) throw new Error('No enabled element found'); + const { viewport, renderingEngine } = enabledElement; + + this.isDrawing = true; + + const camera = viewport.getCamera(); + const { viewPlaneNormal, viewUp } = camera; + if (!viewPlaneNormal || !viewUp) throw new Error('Camera not found'); + + const referencedImageId = this.getReferencedImageId( + viewport, + worldPos, + viewPlaneNormal, + viewUp + ); + + const annotation = { + highlighted: true, + invalidated: true, + metadata: { + toolName: this.getToolName(), + viewPlaneNormal: [...viewPlaneNormal], + viewUp: [...viewUp], + FrameOfReferenceUID: viewport.getFrameOfReferenceUID(), + referencedImageId, + }, + data: { + label: '', + handles: { + points: [[...worldPos]] as [Types.Point3], + activeHandleIndex: null, + textBox: { + hasMoved: false, + worldPosition: [0, 0, 0], + worldBoundingBox: { + topLeft: [0, 0, 0], + topRight: [0, 0, 0], + bottomLeft: [0, 0, 0], + bottomRight: [0, 0, 0], + }, + }, + }, + }, + }; + + const annotationId = this._addAnnotation(element, annotation); + + if (annotationId === null) return; + + const viewportIdsToRender = getViewportIdsWithToolToRender( + element, + this.getToolName(), + false + ); + + triggerAnnotationRenderForViewportIds(renderingEngine, viewportIdsToRender); + }; + + //custom addAnnotations to make sure there is never more than one cursor Annotation + _addAnnotation( + element: HTMLDivElement, + annotation: Annotation + ): string | null { + const annotations = getAnnotations(element, this.getToolName()); + if (annotations instanceof Array && annotations.length > 0) return null; + return addAnnotation(element, annotation); + } + + getActiveAnnotation(element: HTMLDivElement): null | Annotation { + const annotations = getAnnotations(element, this.getToolName()); + if (annotations === undefined || annotations.length === 0) { + return null; + } + const targetAnnotation = annotations[0]; + return targetAnnotation; + } + + /** + * updates the position of the annotation to match the currently set world position + */ + updateAnnotationPosition( + element: HTMLDivElement, + annotation: Annotation + ): void { + const worldPos = this._currentCursorWorldPosition; + if (!worldPos) return; + if (!annotation.data?.handles?.points) return; + annotation.data.handles.points = [[...worldPos]]; + annotation.invalidated = true; + + const viewportIdsToRender = getViewportIdsWithToolToRender( + element, + this.getToolName(), + false + ); + const enabledElement = getEnabledElement(element); + if (!enabledElement) return; + const { renderingEngine } = enabledElement; + triggerAnnotationRenderForViewportIds(renderingEngine, viewportIdsToRender); + } + + //checks if we need to update the annotation position due to camera changes + onCameraModified = (evt: any): void => { + const eventDetail = evt.detail; + const { element, previousCamera, camera } = eventDetail; + const enabledElement = getEnabledElement(element); + const viewport = enabledElement.viewport as + | Types.IVolumeViewport + | Types.IStackViewport; + + //only react to changes for element with cursor, otherwise would cause infinite loop + if (element !== this._elementWithCursor) return; + //check if camera moved along its normal + const oldFocalPoint = previousCamera.focalPoint; + const cameraNormal = camera.viewPlaneNormal; + const newFocalPoint = camera.focalPoint; + + const deltaCameraFocalPoint: Types.Point3 = [0, 0, 0]; + vtkMath.subtract(newFocalPoint, oldFocalPoint, deltaCameraFocalPoint); + //check if focal point changed + if (deltaCameraFocalPoint.reduce((a, b) => a + b, 0) === 0) return; + //if nomrmal is perpendicular to focal point change, then we are not moving along the normal + const dotProduct = vtkMath.dot(deltaCameraFocalPoint, cameraNormal); + //dot product is 0 -> perpendicular + if (Math.abs(dotProduct) < 1e-2) return; + + //need to update the position of the annotation since camera changed + if (!this._currentCanvasPosition) return; + + const newWorldPos = viewport.canvasToWorld(this._currentCanvasPosition); + this._currentCursorWorldPosition = newWorldPos; + this.updateAnnotationPosition(element, this.getActiveAnnotation(element)); + }; + + //display annotation if current viewing plane has a max distance of "displayThreshold" from the annotation + filterInteractableAnnotationsForElement( + element: HTMLDivElement, + annotations: Annotations + ): Annotations { + //calculate distance of current viewport to annotation + if (!(annotations instanceof Array) || annotations.length === 0) return []; + const annotation = annotations[0]; + const viewport = getEnabledElement(element)?.viewport; + if (!viewport) return []; + const camera = viewport.getCamera(); + const { viewPlaneNormal, focalPoint } = camera; + if (!viewPlaneNormal || !focalPoint) return []; + const points = annotation.data?.handles?.points; + if (!(points instanceof Array) || points.length !== 1) return []; + const worldPos = points[0]; + const plane = utilities.planar.planeEquation(viewPlaneNormal, focalPoint); + const distance = utilities.planar.planeDistanceToPoint(plane, worldPos); + return distance < this.configuration.displayThreshold ? [annotation] : []; + } + + /** + * Draws the cursor representation on the enabledElement + * Checks if a stack change has happened and updates annotation in that case + * + * @param enabledElement - The Cornerstone's enabledElement. + * @param svgDrawingHelper - The svgDrawingHelper providing the context for drawing. + */ + renderAnnotation = ( + enabledElement: Types.IEnabledElement, + svgDrawingHelper: SVGDrawingHelper + ): boolean => { + let renderStatus = false; + const { viewport } = enabledElement; + + const isElementWithCursor = this._elementWithCursor === viewport.element; + + //update stack position if position sync is enabled + if (this.configuration.positionSync && !isElementWithCursor) { + this.updateViewportImage(viewport); + } + + const { element } = viewport; + + let annotations = getAnnotations(element, this.getToolName()); + + if (!annotations?.length) { + return renderStatus; + } + + //the viewport change from updateStackPosition might not be applied yet, so sometimes the annotation might not be immediately visible + annotations = this.filterInteractableAnnotationsForElement( + element, + annotations + ) as Annotations; + + if (!annotations?.length) { + return renderStatus; + } + + const styleSpecifier: StyleSpecifier = { + toolGroupId: this.toolGroupId, + toolName: this.getToolName(), + viewportId: enabledElement.viewport.id, + }; + + for (let i = 0; i < annotations.length; i++) { + const annotation = annotations[i] as ReferenceCursor; + const { annotationUID, data } = annotation; + const { handles } = data; + const { points } = handles; + + if (!annotationUID) return renderStatus; + styleSpecifier.annotationUID = annotationUID; + + const lineWidthBase = parseFloat( + this.getStyle('lineWidth', styleSpecifier, annotation) as string + ); + + const lineWidth = + typeof lineWidthBase === 'number' && isElementWithCursor + ? lineWidthBase + : lineWidthBase; + const lineDash = this.getStyle('lineDash', styleSpecifier, annotation); + const color = this.getStyle('color', styleSpecifier, annotation); + + if (points[0].some((e) => isNaN(e))) return renderStatus; + const canvasCoordinates = points.map((p) => + viewport.worldToCanvas(p) + ) as [Types.Point2]; + + // If rendering engine has been destroyed while rendering + if (!viewport.getRenderingEngine()) { + console.warn('Rendering Engine has been destroyed'); + return renderStatus; + } + + if (!isAnnotationVisible(annotationUID)) { + continue; + } + + const crosshairUIDs = { + upper: 'upper', + right: 'right', + lower: 'lower', + left: 'left', + }; + const [x, y] = canvasCoordinates[0]; + const centerSpace = isElementWithCursor ? 20 : 7; + const lineLength = isElementWithCursor ? 5 : 7; + drawLine( + svgDrawingHelper, + annotationUID, + crosshairUIDs.upper, + [x, y - (centerSpace / 2 + lineLength)], + [x, y - centerSpace / 2], + { color, lineDash, lineWidth } + ); + drawLine( + svgDrawingHelper, + annotationUID, + crosshairUIDs.lower, + [x, y + (centerSpace / 2 + lineLength)], + [x, y + centerSpace / 2], + { color, lineDash, lineWidth } + ); + drawLine( + svgDrawingHelper, + annotationUID, + crosshairUIDs.right, + [x + (centerSpace / 2 + lineLength), y], + [x + centerSpace / 2, y], + { color, lineDash, lineWidth } + ); + drawLine( + svgDrawingHelper, + annotationUID, + crosshairUIDs.left, + [x - (centerSpace / 2 + lineLength), y], + [x - centerSpace / 2, y], + { color, lineDash, lineWidth } + ); + renderStatus = true; + } + + return renderStatus; + }; + + updateViewportImage( + viewport: Types.IStackViewport | Types.IVolumeViewport + ): void { + const currentMousePosition = this._currentCursorWorldPosition; + + if (!currentMousePosition || currentMousePosition.some((e) => isNaN(e))) + return; + + if (viewport instanceof StackViewport) { + const closestIndex = utilities.getClosestStackImageIndexForPoint( + currentMousePosition, + viewport + ); + + if (closestIndex === null) return; + if (closestIndex !== viewport.getCurrentImageIdIndex()) + viewport.setImageIdIndex(closestIndex); + } else if (viewport instanceof VolumeViewport) { + const { focalPoint, viewPlaneNormal } = viewport.getCamera(); + if (!focalPoint || !viewPlaneNormal) return; + const plane = utilities.planar.planeEquation(viewPlaneNormal, focalPoint); + const currentDistance = utilities.planar.planeDistanceToPoint( + plane, + currentMousePosition, + true + ); + + if (Math.abs(currentDistance) < 0.5) return; + const normalizedViewPlane = vec3.normalize( + vec3.create(), + vec3.fromValues(...viewPlaneNormal) + ); + const scaledPlaneNormal = vec3.scale( + vec3.create(), + normalizedViewPlane, + currentDistance + ); + const newFocalPoint = vec3.add( + vec3.create(), + vec3.fromValues(...focalPoint), + scaledPlaneNormal + ) as Types.Point3; + //TODO: make check if new focal point is within bounds of volume + const isInBounds = true; + if (isInBounds) { + viewport.setCamera({ focalPoint: newFocalPoint }); + const renderingEngine = viewport.getRenderingEngine(); + if (renderingEngine) renderingEngine.renderViewport(viewport.id); + } + } + } +} + +ReferenceCursors.toolName = 'ReferenceCursors'; +export default ReferenceCursors; diff --git a/packages/tools/src/tools/index.ts b/packages/tools/src/tools/index.ts index fa5e411c7..700f4427b 100644 --- a/packages/tools/src/tools/index.ts +++ b/packages/tools/src/tools/index.ts @@ -20,6 +20,7 @@ import EllipticalROITool from './annotation/EllipticalROITool'; import PlanarFreehandROITool from './annotation/PlanarFreehandROITool'; import ArrowAnnotateTool from './annotation/ArrowAnnotateTool'; import AngleTool from './annotation/AngleTool'; +import ReferenceCursors from './ReferenceCursors'; import ReferenceLines from './ReferenceLinesTool'; // Segmentation DisplayTool @@ -58,6 +59,7 @@ export { PlanarFreehandROITool, ArrowAnnotateTool, AngleTool, + ReferenceCursors, // Segmentations Display SegmentationDisplayTool, // Segmentations Tools diff --git a/packages/tools/src/types/ToolSpecificAnnotationTypes.ts b/packages/tools/src/types/ToolSpecificAnnotationTypes.ts index ce2263bf5..d471993ab 100644 --- a/packages/tools/src/types/ToolSpecificAnnotationTypes.ts +++ b/packages/tools/src/types/ToolSpecificAnnotationTypes.ts @@ -257,6 +257,14 @@ export interface AngleAnnotation extends Annotation { }; } +export interface ReferenceCursor extends Annotation { + data: { + handles: { + points: [Types.Point3]; + }; + }; +} + export interface ReferenceLineAnnotation extends Annotation { data: { handles: { diff --git a/utils/ExampleRunner/example-info.json b/utils/ExampleRunner/example-info.json index 535a3c909..9a29d04a9 100644 --- a/utils/ExampleRunner/example-info.json +++ b/utils/ExampleRunner/example-info.json @@ -165,6 +165,10 @@ "volumeViewportOrientation": { "name": "Volume Viewport Orientation", "description": "Demonstrates you can switch between different orientation of a volume viewport" + }, + "referenceCursors": { + "name": "Referencing Cursors", + "description": "Demonstrates how to synchronize the cursor between multiple viewports" } }, "tools-advanced": { From 64e4ab3a24c3b1b23865b250019ad688b0ce6987 Mon Sep 17 00:00:00 2001 From: ohif-bot Date: Wed, 23 Nov 2022 15:51:26 +0000 Subject: [PATCH 06/25] chore(release): publish [skip ci] - @cornerstonejs/core@0.22.0 - docs@0.7.10 - @cornerstonejs/streaming-image-volume-loader@0.6.7 - @cornerstonejs/tools@0.30.0 --- packages/core/CHANGELOG.md | 11 +++++++++++ packages/core/package.json | 2 +- packages/docs/CHANGELOG.md | 8 ++++++++ packages/docs/package.json | 8 ++++---- packages/streaming-image-volume-loader/CHANGELOG.md | 8 ++++++++ packages/streaming-image-volume-loader/package.json | 4 ++-- packages/tools/CHANGELOG.md | 11 +++++++++++ packages/tools/package.json | 4 ++-- 8 files changed, 47 insertions(+), 9 deletions(-) diff --git a/packages/core/CHANGELOG.md b/packages/core/CHANGELOG.md index cd39e21b5..8d35773fb 100644 --- a/packages/core/CHANGELOG.md +++ b/packages/core/CHANGELOG.md @@ -3,6 +3,17 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [0.22.0](https://github.com/cornerstonejs/cornerstone3D-beta/compare/@cornerstonejs/core@0.21.5...@cornerstonejs/core@0.22.0) (2022-11-23) + + +### Features + +* add referenceCursors tool ([#275](https://github.com/cornerstonejs/cornerstone3D-beta/issues/275)) ([3303246](https://github.com/cornerstonejs/cornerstone3D-beta/commit/3303246836c81efb51e5d5e70c1a8801fbcb019a)) + + + + + ## [0.21.5](https://github.com/cornerstonejs/cornerstone3D-beta/compare/@cornerstonejs/core@0.21.4...@cornerstonejs/core@0.21.5) (2022-11-23) diff --git a/packages/core/package.json b/packages/core/package.json index d92f48947..e84d19812 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "@cornerstonejs/core", - "version": "0.21.5", + "version": "0.22.0", "description": "", "main": "dist/umd/index.js", "types": "dist/esm/index.d.ts", diff --git a/packages/docs/CHANGELOG.md b/packages/docs/CHANGELOG.md index eec619fe5..fdcc9bcee 100644 --- a/packages/docs/CHANGELOG.md +++ b/packages/docs/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.7.10](https://github.com/cornerstonejs/cornerstone3D-beta/compare/docs@0.7.9...docs@0.7.10) (2022-11-23) + +**Note:** Version bump only for package docs + + + + + ## [0.7.9](https://github.com/cornerstonejs/cornerstone3D-beta/compare/docs@0.7.8...docs@0.7.9) (2022-11-23) **Note:** Version bump only for package docs diff --git a/packages/docs/package.json b/packages/docs/package.json index 733c4472d..f7ce8e9f9 100644 --- a/packages/docs/package.json +++ b/packages/docs/package.json @@ -1,6 +1,6 @@ { "name": "docs", - "version": "0.7.9", + "version": "0.7.10", "private": true, "repository": "https://github.com/cornerstonejs/cornerstone3D-beta", "scripts": { @@ -28,9 +28,9 @@ "write-heading-ids": "docusaurus write-heading-ids" }, "dependencies": { - "@cornerstonejs/core": "^0.21.5", - "@cornerstonejs/streaming-image-volume-loader": "^0.6.6", - "@cornerstonejs/tools": "^0.29.8", + "@cornerstonejs/core": "^0.22.0", + "@cornerstonejs/streaming-image-volume-loader": "^0.6.7", + "@cornerstonejs/tools": "^0.30.0", "@docusaurus/core": "2.0.0-rc.1", "@docusaurus/module-type-aliases": "2.0.0-rc.1", "@docusaurus/preset-classic": "2.0.0-rc.1", diff --git a/packages/streaming-image-volume-loader/CHANGELOG.md b/packages/streaming-image-volume-loader/CHANGELOG.md index aa1e4c31d..3b5dac534 100644 --- a/packages/streaming-image-volume-loader/CHANGELOG.md +++ b/packages/streaming-image-volume-loader/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.6.7](https://github.com/cornerstonejs/cornerstone3D-beta/compare/@cornerstonejs/streaming-image-volume-loader@0.6.6...@cornerstonejs/streaming-image-volume-loader@0.6.7) (2022-11-23) + +**Note:** Version bump only for package @cornerstonejs/streaming-image-volume-loader + + + + + ## [0.6.6](https://github.com/cornerstonejs/cornerstone3D-beta/compare/@cornerstonejs/streaming-image-volume-loader@0.6.5...@cornerstonejs/streaming-image-volume-loader@0.6.6) (2022-11-23) **Note:** Version bump only for package @cornerstonejs/streaming-image-volume-loader diff --git a/packages/streaming-image-volume-loader/package.json b/packages/streaming-image-volume-loader/package.json index a72eddb11..95007680d 100644 --- a/packages/streaming-image-volume-loader/package.json +++ b/packages/streaming-image-volume-loader/package.json @@ -1,6 +1,6 @@ { "name": "@cornerstonejs/streaming-image-volume-loader", - "version": "0.6.6", + "version": "0.6.7", "description": "", "main": "dist/umd/index.js", "types": "dist/esm/index.d.ts", @@ -26,7 +26,7 @@ "webpack:watch": "webpack --mode development --progress --watch --config ./.webpack/webpack.dev.js" }, "dependencies": { - "@cornerstonejs/core": "^0.21.5", + "@cornerstonejs/core": "^0.22.0", "cornerstone-wado-image-loader": "^4.2.1" }, "peerDependencies": { diff --git a/packages/tools/CHANGELOG.md b/packages/tools/CHANGELOG.md index 637ca58ca..873da47ce 100644 --- a/packages/tools/CHANGELOG.md +++ b/packages/tools/CHANGELOG.md @@ -3,6 +3,17 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [0.30.0](https://github.com/cornerstonejs/cornerstone3D-beta/compare/@cornerstonejs/tools@0.29.8...@cornerstonejs/tools@0.30.0) (2022-11-23) + + +### Features + +* add referenceCursors tool ([#275](https://github.com/cornerstonejs/cornerstone3D-beta/issues/275)) ([3303246](https://github.com/cornerstonejs/cornerstone3D-beta/commit/3303246836c81efb51e5d5e70c1a8801fbcb019a)) + + + + + ## [0.29.8](https://github.com/cornerstonejs/cornerstone3D-beta/compare/@cornerstonejs/tools@0.29.7...@cornerstonejs/tools@0.29.8) (2022-11-23) **Note:** Version bump only for package @cornerstonejs/tools diff --git a/packages/tools/package.json b/packages/tools/package.json index 9e40ef216..2d33f10d3 100644 --- a/packages/tools/package.json +++ b/packages/tools/package.json @@ -1,6 +1,6 @@ { "name": "@cornerstonejs/tools", - "version": "0.29.8", + "version": "0.30.0", "description": "Cornerstone3D Tools", "main": "dist/umd/index.js", "types": "dist/esm/index.d.ts", @@ -26,7 +26,7 @@ "webpack:watch": "webpack --mode development --progress --watch --config ./.webpack/webpack.dev.js" }, "dependencies": { - "@cornerstonejs/core": "^0.21.5", + "@cornerstonejs/core": "^0.22.0", "lodash.clonedeep": "4.5.0", "lodash.get": "^4.4.2" }, From 1350eca3cdc8d456642c6497dd2b2460a3584c7e Mon Sep 17 00:00:00 2001 From: Neil Date: Wed, 23 Nov 2022 21:08:25 -0500 Subject: [PATCH 07/25] fix: ZoomTool fix for polyData actors with no imageData (#308) --- packages/tools/src/tools/ZoomTool.ts | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/packages/tools/src/tools/ZoomTool.ts b/packages/tools/src/tools/ZoomTool.ts index c7254330b..e84269556 100644 --- a/packages/tools/src/tools/ZoomTool.ts +++ b/packages/tools/src/tools/ZoomTool.ts @@ -151,9 +151,13 @@ class ZoomTool extends BaseTool { // If it is a regular GPU accelerated viewport, then parallel scale // has a physical meaning and we can use that to determine the threshold + // Added spacing preset in case there is no imageData on viewport const imageData = viewport.getImageData(); + let spacing = [1, 1, 1]; + if (imageData) { + spacing = imageData.spacing; + } - const { spacing } = imageData; const { minZoomScale, maxZoomScale } = this.configuration; const t = element.clientHeight * spacing[1] * 0.5; @@ -162,12 +166,14 @@ class ZoomTool extends BaseTool { let cappedParallelScale = parallelScaleToSet; let thresholdExceeded = false; - if (scale < minZoomScale) { - cappedParallelScale = t / minZoomScale; - thresholdExceeded = true; - } else if (scale >= maxZoomScale) { - cappedParallelScale = t / maxZoomScale; - thresholdExceeded = true; + if (imageData) { + if (scale < minZoomScale) { + cappedParallelScale = t / minZoomScale; + thresholdExceeded = true; + } else if (scale >= maxZoomScale) { + cappedParallelScale = t / maxZoomScale; + thresholdExceeded = true; + } } viewport.setCamera({ From b80ea407eaa59da10fe06d0794b0356416410fdc Mon Sep 17 00:00:00 2001 From: ohif-bot Date: Thu, 24 Nov 2022 02:12:50 +0000 Subject: [PATCH 08/25] chore(release): publish [skip ci] - docs@0.7.11 - @cornerstonejs/tools@0.30.1 --- packages/docs/CHANGELOG.md | 8 ++++++++ packages/docs/package.json | 4 ++-- packages/tools/CHANGELOG.md | 11 +++++++++++ packages/tools/package.json | 2 +- 4 files changed, 22 insertions(+), 3 deletions(-) diff --git a/packages/docs/CHANGELOG.md b/packages/docs/CHANGELOG.md index fdcc9bcee..eeabef00c 100644 --- a/packages/docs/CHANGELOG.md +++ b/packages/docs/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.7.11](https://github.com/cornerstonejs/cornerstone3D-beta/compare/docs@0.7.10...docs@0.7.11) (2022-11-24) + +**Note:** Version bump only for package docs + + + + + ## [0.7.10](https://github.com/cornerstonejs/cornerstone3D-beta/compare/docs@0.7.9...docs@0.7.10) (2022-11-23) **Note:** Version bump only for package docs diff --git a/packages/docs/package.json b/packages/docs/package.json index f7ce8e9f9..1307691a7 100644 --- a/packages/docs/package.json +++ b/packages/docs/package.json @@ -1,6 +1,6 @@ { "name": "docs", - "version": "0.7.10", + "version": "0.7.11", "private": true, "repository": "https://github.com/cornerstonejs/cornerstone3D-beta", "scripts": { @@ -30,7 +30,7 @@ "dependencies": { "@cornerstonejs/core": "^0.22.0", "@cornerstonejs/streaming-image-volume-loader": "^0.6.7", - "@cornerstonejs/tools": "^0.30.0", + "@cornerstonejs/tools": "^0.30.1", "@docusaurus/core": "2.0.0-rc.1", "@docusaurus/module-type-aliases": "2.0.0-rc.1", "@docusaurus/preset-classic": "2.0.0-rc.1", diff --git a/packages/tools/CHANGELOG.md b/packages/tools/CHANGELOG.md index 873da47ce..b145f3c9a 100644 --- a/packages/tools/CHANGELOG.md +++ b/packages/tools/CHANGELOG.md @@ -3,6 +3,17 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.30.1](https://github.com/cornerstonejs/cornerstone3D-beta/compare/@cornerstonejs/tools@0.30.0...@cornerstonejs/tools@0.30.1) (2022-11-24) + + +### Bug Fixes + +* ZoomTool fix for polyData actors with no imageData ([#308](https://github.com/cornerstonejs/cornerstone3D-beta/issues/308)) ([1350eca](https://github.com/cornerstonejs/cornerstone3D-beta/commit/1350eca3cdc8d456642c6497dd2b2460a3584c7e)) + + + + + # [0.30.0](https://github.com/cornerstonejs/cornerstone3D-beta/compare/@cornerstonejs/tools@0.29.8...@cornerstonejs/tools@0.30.0) (2022-11-23) diff --git a/packages/tools/package.json b/packages/tools/package.json index 2d33f10d3..3b565752f 100644 --- a/packages/tools/package.json +++ b/packages/tools/package.json @@ -1,6 +1,6 @@ { "name": "@cornerstonejs/tools", - "version": "0.30.0", + "version": "0.30.1", "description": "Cornerstone3D Tools", "main": "dist/umd/index.js", "types": "dist/esm/index.d.ts", From ea8e32a768d3f2d43fc0f1bc9b29388101825ad2 Mon Sep 17 00:00:00 2001 From: Edward Son Date: Wed, 30 Nov 2022 21:18:39 -0500 Subject: [PATCH 09/25] fix: If planar annotation is not visible, filter it (#318) Co-authored-by: edward65 --- packages/tools/src/tools/annotation/PlanarFreehandROITool.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/tools/src/tools/annotation/PlanarFreehandROITool.ts b/packages/tools/src/tools/annotation/PlanarFreehandROITool.ts index fa0eca533..a296a0f5c 100644 --- a/packages/tools/src/tools/annotation/PlanarFreehandROITool.ts +++ b/packages/tools/src/tools/annotation/PlanarFreehandROITool.ts @@ -510,6 +510,10 @@ class PlanarFreehandROITool extends AnnotationTool { const data = annotation.data; const point = data.polyline[0]; + if (!annotation.isVisible) { + continue; + } + // A = point // B = focal point // P = normal From 70e4ffa0c28ed293473c6674d7b158c644f9b1be Mon Sep 17 00:00:00 2001 From: ramonemiliani93 Date: Wed, 30 Nov 2022 21:19:56 -0500 Subject: [PATCH 10/25] fix: filter planarFreeHandeROI based on parallel normals instead of equal normals. (#315) Co-authored-by: Ramon Emiliani --- .../tools/annotation/PlanarFreehandROITool.ts | 23 ++++++++++++++----- 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/packages/tools/src/tools/annotation/PlanarFreehandROITool.ts b/packages/tools/src/tools/annotation/PlanarFreehandROITool.ts index a296a0f5c..de08aac1e 100644 --- a/packages/tools/src/tools/annotation/PlanarFreehandROITool.ts +++ b/packages/tools/src/tools/annotation/PlanarFreehandROITool.ts @@ -1,4 +1,5 @@ import { + CONSTANTS, getEnabledElement, triggerEvent, eventTarget, @@ -42,7 +43,9 @@ import { PlanarFreehandROIAnnotation } from '../../types/ToolSpecificAnnotationT import { PlanarFreehandROICommonData } from '../../utilities/math/polyline/planarFreehandROIInternalTypes'; const { pointCanProjectOnLine } = polyline; +const { EPSILON } = CONSTANTS; +const PARALLEL_THRESHOLD = 1 - EPSILON; /** * PlanarFreehandROITool lets you draw annotations that define an arbitrarily drawn region. * You can use the PlanarFreehandROITool in all perpendicular views (axial, sagittal, coronal), @@ -488,13 +491,21 @@ class PlanarFreehandROITool extends AnnotationTool { spacingInNormalDirection: number ): Annotations { const { viewPlaneNormal } = camera; - const annotationsWithSameNormal = annotations.filter((td: Annotation) => { - const annotationViewPlaneNormal = td.metadata.viewPlaneNormal; - return csUtils.isEqual(annotationViewPlaneNormal, viewPlaneNormal); - }); + + const annotationsWithParallelNormals = annotations.filter( + (td: Annotation) => { + const annotationViewPlaneNormal = td.metadata.viewPlaneNormal; + + const isParallel = + Math.abs(vec3.dot(viewPlaneNormal, annotationViewPlaneNormal)) > + PARALLEL_THRESHOLD; + + return annotationViewPlaneNormal && isParallel; + } + ); // No in plane annotations. - if (!annotationsWithSameNormal.length) { + if (!annotationsWithParallelNormals.length) { return []; } @@ -506,7 +517,7 @@ class PlanarFreehandROITool extends AnnotationTool { const annotationsWithinSlice = []; - for (const annotation of annotationsWithSameNormal) { + for (const annotation of annotationsWithParallelNormals) { const data = annotation.data; const point = data.polyline[0]; From 6e8e51b4b3dde358134fcc7493237a59bec687ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=88=E5=AE=9A=E8=8B=97?= <359650098@qq.com> Date: Thu, 1 Dec 2022 10:23:25 +0800 Subject: [PATCH 11/25] fix: get correct imageData with targetId in BaseTool (#294) * limit disabled element not need to render * Update BaseTool.ts fix: get correct viewport when there are multiple viewport with same stack data Co-authored-by: chendingmiao --- packages/tools/src/tools/base/BaseTool.ts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/packages/tools/src/tools/base/BaseTool.ts b/packages/tools/src/tools/base/BaseTool.ts index 3543c09b0..d34926875 100644 --- a/packages/tools/src/tools/base/BaseTool.ts +++ b/packages/tools/src/tools/base/BaseTool.ts @@ -144,7 +144,7 @@ abstract class BaseTool implements IBaseTool { if (targetId.startsWith('imageId:')) { const imageId = targetId.split('imageId:')[1]; const imageURI = utilities.imageIdToURI(imageId); - const viewports = utilities.getViewportsWithImageURI( + let viewports = utilities.getViewportsWithImageURI( imageURI, renderingEngine.id ); @@ -153,6 +153,14 @@ abstract class BaseTool implements IBaseTool { return; } + viewports = viewports.filter((viewport) => { + return viewport.getCurrentImageId() === imageId; + }); + + if (!viewports || !viewports.length) { + return; + } + return viewports[0].getImageData(); } else if (targetId.startsWith('volumeId:')) { const volumeId = targetId.split('volumeId:')[1]; From e68f064c3c803e1dcde90bb5902217761114e52a Mon Sep 17 00:00:00 2001 From: ohif-bot Date: Thu, 1 Dec 2022 02:27:41 +0000 Subject: [PATCH 12/25] chore(release): publish [skip ci] - docs@0.7.12 - @cornerstonejs/tools@0.30.2 --- packages/docs/CHANGELOG.md | 8 ++++++++ packages/docs/package.json | 4 ++-- packages/tools/CHANGELOG.md | 13 +++++++++++++ packages/tools/package.json | 2 +- 4 files changed, 24 insertions(+), 3 deletions(-) diff --git a/packages/docs/CHANGELOG.md b/packages/docs/CHANGELOG.md index eeabef00c..0b2a4a967 100644 --- a/packages/docs/CHANGELOG.md +++ b/packages/docs/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.7.12](https://github.com/cornerstonejs/cornerstone3D-beta/compare/docs@0.7.11...docs@0.7.12) (2022-12-01) + +**Note:** Version bump only for package docs + + + + + ## [0.7.11](https://github.com/cornerstonejs/cornerstone3D-beta/compare/docs@0.7.10...docs@0.7.11) (2022-11-24) **Note:** Version bump only for package docs diff --git a/packages/docs/package.json b/packages/docs/package.json index 1307691a7..0e8315e6f 100644 --- a/packages/docs/package.json +++ b/packages/docs/package.json @@ -1,6 +1,6 @@ { "name": "docs", - "version": "0.7.11", + "version": "0.7.12", "private": true, "repository": "https://github.com/cornerstonejs/cornerstone3D-beta", "scripts": { @@ -30,7 +30,7 @@ "dependencies": { "@cornerstonejs/core": "^0.22.0", "@cornerstonejs/streaming-image-volume-loader": "^0.6.7", - "@cornerstonejs/tools": "^0.30.1", + "@cornerstonejs/tools": "^0.30.2", "@docusaurus/core": "2.0.0-rc.1", "@docusaurus/module-type-aliases": "2.0.0-rc.1", "@docusaurus/preset-classic": "2.0.0-rc.1", diff --git a/packages/tools/CHANGELOG.md b/packages/tools/CHANGELOG.md index b145f3c9a..f17b72c80 100644 --- a/packages/tools/CHANGELOG.md +++ b/packages/tools/CHANGELOG.md @@ -3,6 +3,19 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.30.2](https://github.com/cornerstonejs/cornerstone3D-beta/compare/@cornerstonejs/tools@0.30.1...@cornerstonejs/tools@0.30.2) (2022-12-01) + + +### Bug Fixes + +* filter planarFreeHandeROI based on parallel normals instead of equal normals. ([#315](https://github.com/cornerstonejs/cornerstone3D-beta/issues/315)) ([70e4ffa](https://github.com/cornerstonejs/cornerstone3D-beta/commit/70e4ffa0c28ed293473c6674d7b158c644f9b1be)) +* get correct imageData with targetId in BaseTool ([#294](https://github.com/cornerstonejs/cornerstone3D-beta/issues/294)) ([6e8e51b](https://github.com/cornerstonejs/cornerstone3D-beta/commit/6e8e51b4b3dde358134fcc7493237a59bec687ab)) +* If planar annotation is not visible, filter it ([#318](https://github.com/cornerstonejs/cornerstone3D-beta/issues/318)) ([ea8e32a](https://github.com/cornerstonejs/cornerstone3D-beta/commit/ea8e32a768d3f2d43fc0f1bc9b29388101825ad2)) + + + + + ## [0.30.1](https://github.com/cornerstonejs/cornerstone3D-beta/compare/@cornerstonejs/tools@0.30.0...@cornerstonejs/tools@0.30.1) (2022-11-24) diff --git a/packages/tools/package.json b/packages/tools/package.json index 3b565752f..d7e2484ed 100644 --- a/packages/tools/package.json +++ b/packages/tools/package.json @@ -1,6 +1,6 @@ { "name": "@cornerstonejs/tools", - "version": "0.30.1", + "version": "0.30.2", "description": "Cornerstone3D Tools", "main": "dist/umd/index.js", "types": "dist/esm/index.d.ts", From 48bd8a14b81e31cba9f3237b0b68b7082bd66892 Mon Sep 17 00:00:00 2001 From: Bill Wallace Date: Wed, 30 Nov 2022 21:33:18 -0500 Subject: [PATCH 13/25] fix: htj2k and keymodifier (#313) * fix(htj2k):Support htj2k in the streaming volume loader * fix(decodeImage):Fix htj2k image decode and mouse key modifiers * Update for PR * update ci build --- common/reviews/api/tools.api.md | 16 +++++++++- .../src/sharedArrayBufferImageLoader.ts | 2 ++ packages/tools/src/enums/ToolBindings.ts | 7 +++++ .../shared/getActiveToolForMouseEvent.ts | 5 +++- .../shared/getMouseModifier.ts | 30 +++++++++++++++++++ .../segmentation/triggerSegmentationRender.ts | 4 +++ 6 files changed, 62 insertions(+), 2 deletions(-) create mode 100644 packages/tools/src/eventDispatchers/shared/getMouseModifier.ts diff --git a/common/reviews/api/tools.api.md b/common/reviews/api/tools.api.md index b89234a61..db9825d1c 100644 --- a/common/reviews/api/tools.api.md +++ b/common/reviews/api/tools.api.md @@ -2606,9 +2606,23 @@ enum KeyboardBindings { // (undocumented) Alt = 18, // (undocumented) + AltMeta = 1891, + // (undocumented) Ctrl = 17, // (undocumented) - Shift = 16 + CtrlAlt = 1718, + // (undocumented) + CtrlMeta = 1791, + // (undocumented) + Meta = 91, + // (undocumented) + Shift = 16, + // (undocumented) + ShiftAlt = 1618, + // (undocumented) + ShiftCtrl = 1617, + // (undocumented) + ShiftMeta = 1691 } // @public (undocumented) diff --git a/packages/streaming-image-volume-loader/src/sharedArrayBufferImageLoader.ts b/packages/streaming-image-volume-loader/src/sharedArrayBufferImageLoader.ts index e6365286a..de45a0fe4 100644 --- a/packages/streaming-image-volume-loader/src/sharedArrayBufferImageLoader.ts +++ b/packages/streaming-image-volume-loader/src/sharedArrayBufferImageLoader.ts @@ -129,6 +129,8 @@ function getTransferSyntaxForContentType(contentType: string): string { 'image/x-jls': '1.2.840.10008.1.2.4.80', 'image/jp2': '1.2.840.10008.1.2.4.90', 'image/jpx': '1.2.840.10008.1.2.4.92', + 'image/jphc': '3.2.840.10008.1.2.4.96', + 'image/jls': '1.2.840.10008.1.2.4.80', }; if (params['transfer-syntax']) { diff --git a/packages/tools/src/enums/ToolBindings.ts b/packages/tools/src/enums/ToolBindings.ts index 548f1aecc..21f5424dd 100644 --- a/packages/tools/src/enums/ToolBindings.ts +++ b/packages/tools/src/enums/ToolBindings.ts @@ -25,6 +25,13 @@ enum KeyboardBindings { Shift = 16, Ctrl = 17, Alt = 18, + Meta = 91, + ShiftCtrl = 1617, + ShiftAlt = 1618, + ShiftMeta = 1691, + CtrlAlt = 1718, + CtrlMeta = 1791, + AltMeta = 1891, } export { MouseBindings, KeyboardBindings }; diff --git a/packages/tools/src/eventDispatchers/shared/getActiveToolForMouseEvent.ts b/packages/tools/src/eventDispatchers/shared/getActiveToolForMouseEvent.ts index a670b0ad9..6fac058a6 100644 --- a/packages/tools/src/eventDispatchers/shared/getActiveToolForMouseEvent.ts +++ b/packages/tools/src/eventDispatchers/shared/getActiveToolForMouseEvent.ts @@ -2,6 +2,7 @@ import { ToolGroupManager } from '../../store'; import { MouseBindings, ToolModes } from '../../enums'; import { keyEventListener } from '../../eventListeners'; import { EventTypes } from '../../types'; +import getMouseModifier from './getMouseModifier'; const { Active } = ToolModes; @@ -22,7 +23,9 @@ export default function getActiveToolForMouseEvent( const mouseEvent = evt.detail.event; // If any keyboard modifier key is also pressed - const modifierKey = keyEventListener.getModifierKey(); + // Use the actual key if set, otherwise get the key from the mouse event. + const modifierKey = + keyEventListener.getModifierKey() || getMouseModifier(mouseEvent); const toolGroup = ToolGroupManager.getToolGroupForViewport( viewportId, diff --git a/packages/tools/src/eventDispatchers/shared/getMouseModifier.ts b/packages/tools/src/eventDispatchers/shared/getMouseModifier.ts new file mode 100644 index 000000000..43259b849 --- /dev/null +++ b/packages/tools/src/eventDispatchers/shared/getMouseModifier.ts @@ -0,0 +1,30 @@ +import { KeyboardBindings as kb } from '../../enums'; + +/** + * Gets the mouse modifier key from a mouse event. + * Supports Shift, Ctrl, Alt, in singly and in combinations of 2 + * Supports Meta singly. + */ +const getMouseModifierKey = (evt) => { + // The logic is a hard coded key mapping + if (evt.shiftKey) { + if (evt.ctrlKey) return kb.ShiftCtrl; + if (evt.altKey) return kb.ShiftAlt; + if (evt.metaKey) return kb.ShiftMeta; + return kb.Shift; + } + if (evt.ctrlKey) { + if (evt.altKey) return kb.CtrlAlt; + if (evt.metaKey) return kb.CtrlMeta; + return kb.Ctrl; + } + if (evt.altKey) { + return (evt.metaKey && kb.AltMeta) || kb.Alt; + } + if (evt.metaKey) { + kb.Meta; + } + return undefined; +}; + +export default getMouseModifierKey; diff --git a/packages/tools/src/utilities/segmentation/triggerSegmentationRender.ts b/packages/tools/src/utilities/segmentation/triggerSegmentationRender.ts index 8971e315a..6dd5b3fc3 100644 --- a/packages/tools/src/utilities/segmentation/triggerSegmentationRender.ts +++ b/packages/tools/src/utilities/segmentation/triggerSegmentationRender.ts @@ -129,6 +129,10 @@ class SegmentationRenderingEngine { const segmentationDisplayToolInstance = toolGroup.getToolInstance( SegmentationDisplayTool.toolName ) as SegmentationDisplayTool; + if (!segmentationDisplayToolInstance) { + console.warn('No segmentation tool found inside', toolGroupId); + return; + } function onSegmentationRender(evt: Types.EventTypes.ImageRenderedEvent) { const { element, viewportId, renderingEngineId } = evt.detail; From d7704a9b7174fd66b3f8374709e919b32f9c9ad9 Mon Sep 17 00:00:00 2001 From: ohif-bot Date: Thu, 1 Dec 2022 02:37:29 +0000 Subject: [PATCH 14/25] chore(release): publish [skip ci] - docs@0.7.13 - @cornerstonejs/streaming-image-volume-loader@0.6.8 - @cornerstonejs/tools@0.30.3 --- packages/docs/CHANGELOG.md | 8 ++++++++ packages/docs/package.json | 6 +++--- packages/streaming-image-volume-loader/CHANGELOG.md | 11 +++++++++++ packages/streaming-image-volume-loader/package.json | 2 +- packages/tools/CHANGELOG.md | 11 +++++++++++ packages/tools/package.json | 2 +- 6 files changed, 35 insertions(+), 5 deletions(-) diff --git a/packages/docs/CHANGELOG.md b/packages/docs/CHANGELOG.md index 0b2a4a967..433d1b0c7 100644 --- a/packages/docs/CHANGELOG.md +++ b/packages/docs/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.7.13](https://github.com/cornerstonejs/cornerstone3D-beta/compare/docs@0.7.12...docs@0.7.13) (2022-12-01) + +**Note:** Version bump only for package docs + + + + + ## [0.7.12](https://github.com/cornerstonejs/cornerstone3D-beta/compare/docs@0.7.11...docs@0.7.12) (2022-12-01) **Note:** Version bump only for package docs diff --git a/packages/docs/package.json b/packages/docs/package.json index 0e8315e6f..b8035ddb3 100644 --- a/packages/docs/package.json +++ b/packages/docs/package.json @@ -1,6 +1,6 @@ { "name": "docs", - "version": "0.7.12", + "version": "0.7.13", "private": true, "repository": "https://github.com/cornerstonejs/cornerstone3D-beta", "scripts": { @@ -29,8 +29,8 @@ }, "dependencies": { "@cornerstonejs/core": "^0.22.0", - "@cornerstonejs/streaming-image-volume-loader": "^0.6.7", - "@cornerstonejs/tools": "^0.30.2", + "@cornerstonejs/streaming-image-volume-loader": "^0.6.8", + "@cornerstonejs/tools": "^0.30.3", "@docusaurus/core": "2.0.0-rc.1", "@docusaurus/module-type-aliases": "2.0.0-rc.1", "@docusaurus/preset-classic": "2.0.0-rc.1", diff --git a/packages/streaming-image-volume-loader/CHANGELOG.md b/packages/streaming-image-volume-loader/CHANGELOG.md index 3b5dac534..089430392 100644 --- a/packages/streaming-image-volume-loader/CHANGELOG.md +++ b/packages/streaming-image-volume-loader/CHANGELOG.md @@ -3,6 +3,17 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.6.8](https://github.com/cornerstonejs/cornerstone3D-beta/compare/@cornerstonejs/streaming-image-volume-loader@0.6.7...@cornerstonejs/streaming-image-volume-loader@0.6.8) (2022-12-01) + + +### Bug Fixes + +* htj2k and keymodifier ([#313](https://github.com/cornerstonejs/cornerstone3D-beta/issues/313)) ([48bd8a1](https://github.com/cornerstonejs/cornerstone3D-beta/commit/48bd8a14b81e31cba9f3237b0b68b7082bd66892)) + + + + + ## [0.6.7](https://github.com/cornerstonejs/cornerstone3D-beta/compare/@cornerstonejs/streaming-image-volume-loader@0.6.6...@cornerstonejs/streaming-image-volume-loader@0.6.7) (2022-11-23) **Note:** Version bump only for package @cornerstonejs/streaming-image-volume-loader diff --git a/packages/streaming-image-volume-loader/package.json b/packages/streaming-image-volume-loader/package.json index 95007680d..877416980 100644 --- a/packages/streaming-image-volume-loader/package.json +++ b/packages/streaming-image-volume-loader/package.json @@ -1,6 +1,6 @@ { "name": "@cornerstonejs/streaming-image-volume-loader", - "version": "0.6.7", + "version": "0.6.8", "description": "", "main": "dist/umd/index.js", "types": "dist/esm/index.d.ts", diff --git a/packages/tools/CHANGELOG.md b/packages/tools/CHANGELOG.md index f17b72c80..fea833a05 100644 --- a/packages/tools/CHANGELOG.md +++ b/packages/tools/CHANGELOG.md @@ -3,6 +3,17 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.30.3](https://github.com/cornerstonejs/cornerstone3D-beta/compare/@cornerstonejs/tools@0.30.2...@cornerstonejs/tools@0.30.3) (2022-12-01) + + +### Bug Fixes + +* htj2k and keymodifier ([#313](https://github.com/cornerstonejs/cornerstone3D-beta/issues/313)) ([48bd8a1](https://github.com/cornerstonejs/cornerstone3D-beta/commit/48bd8a14b81e31cba9f3237b0b68b7082bd66892)) + + + + + ## [0.30.2](https://github.com/cornerstonejs/cornerstone3D-beta/compare/@cornerstonejs/tools@0.30.1...@cornerstonejs/tools@0.30.2) (2022-12-01) diff --git a/packages/tools/package.json b/packages/tools/package.json index d7e2484ed..3d6c8c52c 100644 --- a/packages/tools/package.json +++ b/packages/tools/package.json @@ -1,6 +1,6 @@ { "name": "@cornerstonejs/tools", - "version": "0.30.2", + "version": "0.30.3", "description": "Cornerstone3D Tools", "main": "dist/umd/index.js", "types": "dist/esm/index.d.ts", From a85a86785de9f225154829a4934926143c86eb5e Mon Sep 17 00:00:00 2001 From: Alireza Date: Thu, 1 Dec 2022 07:57:28 -0500 Subject: [PATCH 15/25] fix: coronal view should not be flipped (#321) --- .../core/src/constants/mprCameraValues.ts | 2 +- ...eURI_100_100_10_1_1_1_0_coronal_linear.png | Bin 24301 -> 25355 bytes ...URI_100_100_10_1_1_1_0_coronal_nearest.png | Bin 21485 -> 22063 bytes .../volumeURI_100_100_10_1_1_1_0_SEG_COR.png | Bin 3038 -> 3032 bytes ...0_100_10_1_1_1_0_SEG_SphereScissor_COR.png | Bin 3914 -> 3607 bytes .../test/groundTruth/windowLevel_canvas2.png | Bin 3362 -> 4318 bytes 6 files changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/src/constants/mprCameraValues.ts b/packages/core/src/constants/mprCameraValues.ts index 3c2e03154..f564c2f1e 100644 --- a/packages/core/src/constants/mprCameraValues.ts +++ b/packages/core/src/constants/mprCameraValues.ts @@ -10,7 +10,7 @@ const MPR_CAMERA_VALUES = { viewUp: [0, 0, 1], }, coronal: { - viewPlaneNormal: [0, 1, 0], + viewPlaneNormal: [0, -1, 0], viewUp: [0, 0, 1], }, }; diff --git a/packages/core/test/groundTruth/volumeURI_100_100_10_1_1_1_0_coronal_linear.png b/packages/core/test/groundTruth/volumeURI_100_100_10_1_1_1_0_coronal_linear.png index 56dad440c1f6e0a99cae745ca4fae3440d76b6a9..b0058f2398ae549f831c2e71e678d7d092fca314 100644 GIT binary patch literal 25355 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 literal 24301 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 literal 21485 zcmeHPe{hrK9e?xoEqmKq^Onq3Xdx-ZR#b=+X5}2@t*t<<7EVx)iA0Y%oD73w*j`if z=52daT!GHht2oxCC*s&VdOCYWOHEL3aAVrAG3&va=2mDUUE8!KP5L&;yYKTRZ}O(V z&bw`QJNNw2yYzYAJkR(0`F=j%@AK=jZ$stU%=G!`1VLn$uPb|)AW}T&kDdc9550N* z5<%#Q^0Jam)mumZcIw@yAG>Mi#oH?y(yE(ZuUq0)6`s+o)7+_2dv*J4i_;HXD0%1i z=X)27KEg$I4?nl7qQ+dV@GQaaLd-ts!u-)tkhs_4-bqnH z`!Pz$KhtO^aBA|g;JdOIPvgh{ob#e~(hhP7$mc)+Hus|_wE!}+grYZ6C4j*D=s zsB~kRue4dst+5#zCVLf5Rp+XB%QL^VTSERpy_Q?)+hG~%qCTpfo_t*VR1DOUk@_1?PaEpnvN!IFLyb@v&W~es#c9yM`{5fBhY@^4DAqZRrqGC8 z=Nzxe@O8T)^X12mqgF9B>>NwDdt-PxZ=wI(o4AqWwcG5GOxo9%J7aW?&liObxMtPs zG8W3w_%aJ#(O&pr6qQJhuW>_TnFgwybw~1| z6mxSWNj>O2RbAPhs-E!oqpE^$L?#hGEB~QpUI$@Ty{&xA+Uz0duSGMHLj4$w??09* zJYnMYM-{+$^oTH*KaCLlJWyBFv%ID6qA`{xDB!sY__&WD6oqi_GO+n_1gHYEImK_&#V_FAK)~o|Uy{ zEQ_&TYYZH#<7s$1XNzsXKh=kX;qF1|?SUM&&H%R=`Lpo$vJ-K59p*IPvN z2F`ATf18iUz@q+XpZV)_pZSZ(T%b-R^8FPVIf7rm6aw?v?auN4!NCEGMQ+31yr*hI z1(3wI-6l2@ycQ*+-cp%ZP+%Nc5dMaViG#H7^rlF@3IOn%fZ(n#jj{663K$GOS`>MU zHMDz*X-3i(5M0uhR2L3FuLVs9xiXvK=%hN03G#rl?*}r4kmmA0L~C!>iZ8L;gO{~) zVQp;`=TO4P{%~qka_$d670>PYyD{CTdD&7=sKgAR9V8e{5E3z8f?huk2G>%;f72QI zWV@8cS47kJ3+sWA?`6w;mJG=R&i9`K-kZ45Rxs*EOBH5X9l7bi`8U&0NxQHL&^?1{ zkR)V;C|~?t;QU$*l53|K@vSq?H-x6?R^ZfuLP@RxkYfH0S;!LBt_>je1rJGb9Yb{d zLKz8UJZr!A4N0#38j#{UsWBl-nXkGO?5SEI$+f!((A}I7mkPM%-3#`NJtN8W8jgeU z0}RUJtFr){)~?x}gZ1PpKZLbsoY7`h4_bcy?W674+#1Qu;H4>>Y5?xUelj{bD$BfHZ(Xma=Ne7KBSm6F*+v`oE~n=ZEV^y zG0cTm`rnG7@XKvTAQDlf4-T7^)#mjVX_SpDNGfpm^H~xj;6i~5 z1zwiQi%VP~NptGwR|rCL82v6l?6?N)PT*>Zt0k_MxLV?BiK`{9mU!D9PYv*#2%k{l zqd9!c8QW9DTg}SNRlJZ^qJbCBc=3!E&v^0tA6Yy*IHH@mE#)V7@~ diff --git a/packages/tools/test/groundTruth/volumeURI_100_100_10_1_1_1_0_SEG_COR.png b/packages/tools/test/groundTruth/volumeURI_100_100_10_1_1_1_0_SEG_COR.png index 2fa4476655de8c05c5bce2346c8edeab4d04b88a..74283ec8ae96b9642ac9e11d9ccd9f72050f8398 100644 GIT binary patch literal 3032 zcmeAS@N?(olHy`uVBq!ia0vp^zd)FS4M=vpiLzy2V2sRkb`J1#c2)=|%1_J8No8Qr zn3y|ZzqeDM%+dZ-j=ch%%Y>>Nom*Y^$Yh1Eb2i1@;?M4U{dMWVVEyyrSKr(cGjY>> z>zI(7tLxk7TJON?==~t**RQzvrD5McJ+1js^zLavgPy+dp)X7)#B;O-`&X^}%6s)Y z3#;P~Lyp#ulR94fVv78*cJA`9R`*wTm;A}z{AW^DNc-dN?SD_c_!;nc{r<;a(t3o{ z|1OyM;Ls+<^Aa2PWpkgbKEf>4s$@4|PU%mVqFYb9m!(?@D(_R|`yg{#z+RyJQts~@ z>E~x<|4nsV_THFF+cU`CSHMc?g1|AIX&b!m{&~-9zSmGhaRWd1?GsAu(pr1PN_-`b zeAZ}4=2A@Jy=*e;&5!c(wR1I?i`!Y&uaz}!^nPVmaOYO9UGN>j2fsoLy8oKIT3P8G zZ9MC=7wZelqB+}5518{^c`y5X?<0$^#tVK6?pRgJWcxqq-O=zr62P#INsaJK^YvxW z02o16GVKjIa%&%fBcy73f}&HakP87GyDW_0IXZ1x2aF#mMTB#n9CUWaO9R1H-x~ zGubn*B((^r8EPBMfw)yb&9c!41s78ALE;813XB{(E*pJ#MB8yqTj6zqfq{Lur;B4q z1>@U0dnbmZi?CdX6lIekH zzq75~EjMl6y6LaKK9Xl*k&Ak;z5U*~A{LGs(SVY6hXep$7o|NZ!x@!xU(nmso4bzk23>x@tJ#Xnhnd*bD_%fGnp-@4+y`-2vyeJ|8`j)s44SvaNtNq3ywf{&as zf*)M%7kz1-|MBWy#;*S*Q9%1HyCoYiZ5Iysay#;uFN>e{hYR(k-><*Ed~fc-x{VF` zzx3I@9~Z3PY!v^$==ZCAE^q#wF$L<{dgQIOCeUN=R$Es}PpeY^3bAut)?)eR{osQ5 z-_!pKKgIp8`CT7>_x1MQds*ayAN>3zqx-F7Z_N6O-1a^V4s#p!@4D`qmcX>LVbSxn zIDr*F2kbh*u!^L7_eU2-f z8db3=Y-$s@HLCKSu{K&Ux~}g&bAY9Y(e?bf w88Hi=U=^`7|J($$ebK1pqrpDx6Pz96MQiQtM|&0ko9YYzopr0Hl#q@c;k- literal 3038 zcmeAS@N?(olHy`uVBq!ia0vp^zd)FS4M=vpiLzy2V06lKb`J1#c2)=|%1_J8No8Qr zn3$Z9AmpK=aUwA>A>qVXZB4GC@}Q26o}h&KV+YPC98x%^a7tnNjF^}Q5&{pHLk=-4 z4m`L$o2N(N*CxFaYyRc;?EGe*r<>emecI&l-NQ>~yb5Fp_sS|Xn(&YLi04UGS5+RK z9!`gCMU|e69e0^hxxXTyhdOFOIyIIru4W?^ZJ@5 zHck{|X4crFxXS)ML%Z@~33jJ73uZPREHaw#kxy~jjKc>uFiY?=eHKX4Ixi{lLqt-7 zPfy1~$E(G<;iI&7;jE2MsBRtc5eHpZXYz_t%Mm7d!Ad8WK2}lS**^EH8212|&lYs@S zhJnEjNHZ`nEntF+{93?_V1p#VstT z4fPE4;bsH1+JHo@{EISEfi{E8w==W>t3(k)*aJ}?kda@K4-AE(%w*5JlGLJtB4CJs zbsIr-<5q!gR#0kjeoiS!b7)>>i5<)UG+}gI&iT1OON)zTe7W>VBfr0∨B4q1>@U0XM5*76=Qi|mgFd)b7OLgER&Lek^+bO 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 literal 3914 zcmeHKi#L>8AAUz@av%C~&y2oGg>kDeGsvji@5MsNWiVl8%nUP$Ga?xYm5xf|l8&R> zMp>FL0Ks^ zDF6Uu?QCt_!CA2SLM6eGIq8D|0EjOhi*>cbV&Sf2VhBD02LL)3BF|$ST3l6 z9^Pqs3oEhS^0uvRVbByj5qp;_XPxuJ)3(bYr;w0%Ckbwv${W#gy_r^Ut+wE$RzX(b z#@6(=Sdz_CGJL)^*8} z;QBCFD5xKC{gS@`_t2QSSXX5(s1*9>R&-{jSuxt8G)bybpq5HosYNl`ViNfA ziOP&(l_rU3EsGf%#${ubdbLHs*`gJbou+}mMB2<$KZKfSRufLZptF!)PjcdsHH5bt z!*n5D9ov6b4-S^AkqHN@?T7QW3vqG+c7hlRh(jcSbs&NOV&I^-h9Mx<`NpT<0rAfo z0I&dO01zt>`^pFk)_udPn+!GU98e;S_w=TEJ2{#M5hL{jf{B4ReR?EmRREyr<{*s3 zQ3K%g$Or<(oQ~1{tS|@hDvi{Jf0j_gG1}fvu5c`ojDzpdH_$iGwvd9u;b?Mjh`GDX zfi-h*h0zY9Qc31WB#lPXry1)L$)QL?Gcz-!ff3TkNDox#QBDx30dzeAWy=>Q|M;=N zQG&>L5*1G*z*qeS1QMgE7;Wv0Nd<`#kB8slj%FCrz=9A4F;F9m~E4q?esLp!ODaMvrLFX>bSqyzZ$^_z=8ZHw2GB zz!gd)gV0ByRp>X@YbPntS94}C`%%yQ5`jqMzwodk_kl2g!MKM=XtG!G_Me!LNdd&P zpbCT5Rs|49;a+GT6I`hMT>PxT|Ia-*)pb>sA(|K;=Im+=$t&}mddDeEnVOm!773Rf zUX@z2>uYJ_K`Za7xMP`K{ANqWN|Z$pm$8Hs^(I9@C*_x=X_pj*edVDoqxEglv>ZM9 z!uvte-`%%{Uq1-%E7rfu?eABQiHSM0mpG(*_fSI9ZAIHu(e!Xj!1B|1o^A)r%-F9X z?nHI`@;q-@uK5PBFf*e^P8tS^MjodPPfX_DucC~1$SUjmsdVIFQ1d00lgRma{>WqY zf)LCx0Q^uhZ%C?(ag9!0;(5p_ro4B_x6mAFh(`$=T6gTzFm#NrnEvZ&7Qz{yJt&ud z4Y|xZbQ^Vr`%u7fn!Bce8G>Q6&l$7_o~3>Cts=;RY5L9YaNyL(EzPum<`b-7Utizl zm5Jqczrdvxd_!WBn|Q+pcINSjyQPoW)^+ckqv-XPS%%Ii=hzAoF8p126!&?c^epWF-v+qw#%URT@hWZtp^xAg$S4drK>z>|gv%_R64DrJrc z$AKNp8_6HbJ(BKMEp97&w#3kI3$Chehk*%mD>Wd6#vks=V8neAh7M|sX9urLdkkPwha%0yY&!WqsejYG;xJiTTUAe@EK!=h z1HImK7~ix|D=zjTZ>gJ^7h0*Nm5a=uf8T*HQDua|_L(4UIGHE+TSNg~Fqi&QS3Z|NO@H zgC`fpGRx(yXBOSY()(a7y}m3(n-3m-N~*P-(aB6++x0QCPnVmTCCbl_-JJS&${^nu zf;j2t$O=r$C!uG?<(wA-qb@0Ntwhf{^%R7s5wQZ!7KJi&>}=S@Oyjn_nTxqlj=X04 zx!rvq$2(KnGPTAnym$AcF$Uz>2PKcwx?oINYj?4N(tjcdgkMazUKv@~)XH|!cGGlSL$c-rqT)A*oQrvoW(fIKuWrfk0 z5wM-by+VU?dV<{H4wLjf1Mhl1Fs``>Ik}yT&G8~FuQ$ygan&U%ROQo;ZKO4uI?@Ypli zKH_H;;j!#D73o%I0Jd>Ox3=pgfSo2;94T%j&dj@BwCPl#IP<89Y2=pk2&-8cy$8^Z nTdih)rtq^0KkveM#I%3HLQQDkt$nM1YJlB-XPXl1V~PI-dDK4n diff --git a/packages/tools/test/groundTruth/windowLevel_canvas2.png b/packages/tools/test/groundTruth/windowLevel_canvas2.png index 2acc68b1d34980274710732aeaf3b34c4f465c9c..64c51430f5108ca1ad21d15f22e4e321c151d145 100644 GIT binary patch literal 4318 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 literal 3362 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 EHn+a From 027befd1f18b9619bb1992b7eff3e0658f478e7e Mon Sep 17 00:00:00 2001 From: ohif-bot Date: Thu, 1 Dec 2022 13:02:12 +0000 Subject: [PATCH 16/25] chore(release): publish [skip ci] - @cornerstonejs/core@0.22.1 - docs@0.7.14 - @cornerstonejs/streaming-image-volume-loader@0.6.9 - @cornerstonejs/tools@0.30.4 --- packages/core/CHANGELOG.md | 11 +++++++++++ packages/core/package.json | 2 +- packages/docs/CHANGELOG.md | 8 ++++++++ packages/docs/package.json | 8 ++++---- packages/streaming-image-volume-loader/CHANGELOG.md | 8 ++++++++ packages/streaming-image-volume-loader/package.json | 4 ++-- packages/tools/CHANGELOG.md | 11 +++++++++++ packages/tools/package.json | 4 ++-- 8 files changed, 47 insertions(+), 9 deletions(-) diff --git a/packages/core/CHANGELOG.md b/packages/core/CHANGELOG.md index 8d35773fb..dd3e710a8 100644 --- a/packages/core/CHANGELOG.md +++ b/packages/core/CHANGELOG.md @@ -3,6 +3,17 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.22.1](https://github.com/cornerstonejs/cornerstone3D-beta/compare/@cornerstonejs/core@0.22.0...@cornerstonejs/core@0.22.1) (2022-12-01) + + +### Bug Fixes + +* coronal view should not be flipped ([#321](https://github.com/cornerstonejs/cornerstone3D-beta/issues/321)) ([a85a867](https://github.com/cornerstonejs/cornerstone3D-beta/commit/a85a86785de9f225154829a4934926143c86eb5e)) + + + + + # [0.22.0](https://github.com/cornerstonejs/cornerstone3D-beta/compare/@cornerstonejs/core@0.21.5...@cornerstonejs/core@0.22.0) (2022-11-23) diff --git a/packages/core/package.json b/packages/core/package.json index e84d19812..c6f415365 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "@cornerstonejs/core", - "version": "0.22.0", + "version": "0.22.1", "description": "", "main": "dist/umd/index.js", "types": "dist/esm/index.d.ts", diff --git a/packages/docs/CHANGELOG.md b/packages/docs/CHANGELOG.md index 433d1b0c7..318a2e40b 100644 --- a/packages/docs/CHANGELOG.md +++ b/packages/docs/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.7.14](https://github.com/cornerstonejs/cornerstone3D-beta/compare/docs@0.7.13...docs@0.7.14) (2022-12-01) + +**Note:** Version bump only for package docs + + + + + ## [0.7.13](https://github.com/cornerstonejs/cornerstone3D-beta/compare/docs@0.7.12...docs@0.7.13) (2022-12-01) **Note:** Version bump only for package docs diff --git a/packages/docs/package.json b/packages/docs/package.json index b8035ddb3..a110c47ef 100644 --- a/packages/docs/package.json +++ b/packages/docs/package.json @@ -1,6 +1,6 @@ { "name": "docs", - "version": "0.7.13", + "version": "0.7.14", "private": true, "repository": "https://github.com/cornerstonejs/cornerstone3D-beta", "scripts": { @@ -28,9 +28,9 @@ "write-heading-ids": "docusaurus write-heading-ids" }, "dependencies": { - "@cornerstonejs/core": "^0.22.0", - "@cornerstonejs/streaming-image-volume-loader": "^0.6.8", - "@cornerstonejs/tools": "^0.30.3", + "@cornerstonejs/core": "^0.22.1", + "@cornerstonejs/streaming-image-volume-loader": "^0.6.9", + "@cornerstonejs/tools": "^0.30.4", "@docusaurus/core": "2.0.0-rc.1", "@docusaurus/module-type-aliases": "2.0.0-rc.1", "@docusaurus/preset-classic": "2.0.0-rc.1", diff --git a/packages/streaming-image-volume-loader/CHANGELOG.md b/packages/streaming-image-volume-loader/CHANGELOG.md index 089430392..e80c5ee93 100644 --- a/packages/streaming-image-volume-loader/CHANGELOG.md +++ b/packages/streaming-image-volume-loader/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.6.9](https://github.com/cornerstonejs/cornerstone3D-beta/compare/@cornerstonejs/streaming-image-volume-loader@0.6.8...@cornerstonejs/streaming-image-volume-loader@0.6.9) (2022-12-01) + +**Note:** Version bump only for package @cornerstonejs/streaming-image-volume-loader + + + + + ## [0.6.8](https://github.com/cornerstonejs/cornerstone3D-beta/compare/@cornerstonejs/streaming-image-volume-loader@0.6.7...@cornerstonejs/streaming-image-volume-loader@0.6.8) (2022-12-01) diff --git a/packages/streaming-image-volume-loader/package.json b/packages/streaming-image-volume-loader/package.json index 877416980..3b4f9dc12 100644 --- a/packages/streaming-image-volume-loader/package.json +++ b/packages/streaming-image-volume-loader/package.json @@ -1,6 +1,6 @@ { "name": "@cornerstonejs/streaming-image-volume-loader", - "version": "0.6.8", + "version": "0.6.9", "description": "", "main": "dist/umd/index.js", "types": "dist/esm/index.d.ts", @@ -26,7 +26,7 @@ "webpack:watch": "webpack --mode development --progress --watch --config ./.webpack/webpack.dev.js" }, "dependencies": { - "@cornerstonejs/core": "^0.22.0", + "@cornerstonejs/core": "^0.22.1", "cornerstone-wado-image-loader": "^4.2.1" }, "peerDependencies": { diff --git a/packages/tools/CHANGELOG.md b/packages/tools/CHANGELOG.md index fea833a05..d3675e909 100644 --- a/packages/tools/CHANGELOG.md +++ b/packages/tools/CHANGELOG.md @@ -3,6 +3,17 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.30.4](https://github.com/cornerstonejs/cornerstone3D-beta/compare/@cornerstonejs/tools@0.30.3...@cornerstonejs/tools@0.30.4) (2022-12-01) + + +### Bug Fixes + +* coronal view should not be flipped ([#321](https://github.com/cornerstonejs/cornerstone3D-beta/issues/321)) ([a85a867](https://github.com/cornerstonejs/cornerstone3D-beta/commit/a85a86785de9f225154829a4934926143c86eb5e)) + + + + + ## [0.30.3](https://github.com/cornerstonejs/cornerstone3D-beta/compare/@cornerstonejs/tools@0.30.2...@cornerstonejs/tools@0.30.3) (2022-12-01) diff --git a/packages/tools/package.json b/packages/tools/package.json index 3d6c8c52c..d855d0c9a 100644 --- a/packages/tools/package.json +++ b/packages/tools/package.json @@ -1,6 +1,6 @@ { "name": "@cornerstonejs/tools", - "version": "0.30.3", + "version": "0.30.4", "description": "Cornerstone3D Tools", "main": "dist/umd/index.js", "types": "dist/esm/index.d.ts", @@ -26,7 +26,7 @@ "webpack:watch": "webpack --mode development --progress --watch --config ./.webpack/webpack.dev.js" }, "dependencies": { - "@cornerstonejs/core": "^0.22.0", + "@cornerstonejs/core": "^0.22.1", "lodash.clonedeep": "4.5.0", "lodash.get": "^4.4.2" }, From f973e7262897a2daf4f37363d3e818ae88620bb8 Mon Sep 17 00:00:00 2001 From: Alireza Date: Thu, 1 Dec 2022 08:23:25 -0500 Subject: [PATCH 17/25] fix: bidirectional tool when short and long axis changes (#309) * fix rotation for handles * fix: short axis movement * fix: bidirectional tool incorrect interaction --- common/reviews/api/core.api.md | 6 +- common/reviews/api/tools.api.md | 8 +- package.json | 4 +- packages/core/src/utilities/index.ts | 2 + .../src/tools/annotation/BidirectionalTool.ts | 300 ++++++++++-------- utils/ExampleRunner/example-info.json | 2 +- 6 files changed, 187 insertions(+), 135 deletions(-) diff --git a/common/reviews/api/core.api.md b/common/reviews/api/core.api.md index ebda708ed..9871fdd2c 100644 --- a/common/reviews/api/core.api.md +++ b/common/reviews/api/core.api.md @@ -611,6 +611,9 @@ function getVolumeActorCorners(volumeActor: any): Array; // @public (undocumented) function getVolumeViewportsContainingSameVolumes(targetViewport: IVolumeViewport, renderingEngineId?: string): Array; +// @public (undocumented) +function hasNaNValues(input: number[] | number): boolean; + // @public (undocumented) interface ICache { // (undocumented) @@ -1975,7 +1978,8 @@ declare namespace utilities { getClosestStackImageIndexForPoint, calculateViewportsSpatialRegistration, spatialRegistrationMetadataProvider, - getViewportImageCornersInWorld + getViewportImageCornersInWorld, + hasNaNValues } } export { utilities } diff --git a/common/reviews/api/tools.api.md b/common/reviews/api/tools.api.md index db9825d1c..e5069137c 100644 --- a/common/reviews/api/tools.api.md +++ b/common/reviews/api/tools.api.md @@ -503,8 +503,12 @@ export class BidirectionalTool extends AnnotationTool { hasMoved?: boolean; } | null; // (undocumented) + _getSignedAngle: (vector1: any, vector2: any) => number; + // (undocumented) _getTextLines: (data: any, targetId: any) => string[]; // (undocumented) + _handleDragModify: (evt: any) => void; + // (undocumented) handleSelectedCallback: (evt: EventTypes_2.MouseDownEventType, annotation: BidirectionalAnnotation, handle: ToolHandle, interactionType?: string) => void; // (undocumented) isDrawing: boolean; @@ -521,11 +525,9 @@ export class BidirectionalTool extends AnnotationTool { // (undocumented) _mouseDragModifyCallback: (evt: MouseDragEventType) => void; // (undocumented) - _mouseDragModifyHandle: (evt: any) => void; - // (undocumented) _mouseUpCallback: (evt: EventTypes_2.MouseUpEventType | EventTypes_2.MouseClickEventType) => void; // (undocumented) - _movingLongAxisWouldPutItThroughShortAxis: (proposedFirstLineSegment: any, secondLineSegment: any) => boolean; + _movingLongAxisWouldPutItThroughShortAxis: (firstLineSegment: any, secondLineSegment: any) => boolean; // (undocumented) preventHandleOutsideImage: boolean; // (undocumented) diff --git a/package.json b/package.json index adfceb8b4..3744a1504 100644 --- a/package.json +++ b/package.json @@ -12,10 +12,10 @@ "yarn": ">=1.19.1" }, "scripts": { - "api-check": "lerna run --concurrency 1 api-check ", + "api-check": "lerna run api-check ", "build": "lerna run build --stream", "build:umd": "lerna run build:umd --stream", - "build:update-api": "lerna run --concurrency 1 build:update-api", + "build:update-api": "lerna run build:update-api", "example": "node ./utils/ExampleRunner/example-runner-cli.js", "all-examples": "node ./utils/ExampleRunner/build-all-examples-cli.js --fromRoot", "build-all-examples": "node ./utils/ExampleRunner/build-all-examples-cli.js --build --fromRoot", diff --git a/packages/core/src/utilities/index.ts b/packages/core/src/utilities/index.ts index be6aa7773..6ced41f4d 100644 --- a/packages/core/src/utilities/index.ts +++ b/packages/core/src/utilities/index.ts @@ -31,6 +31,7 @@ import getClosestStackImageIndexForPoint from './getClosestStackImageIndexForPoi import calculateViewportsSpatialRegistration from './calculateViewportsSpatialRegistration'; import spatialRegistrationMetadataProvider from './spatialRegistrationMetadataProvider'; import getViewportImageCornersInWorld from './getViewportImageCornersInWorld'; +import hasNaNValues from './hasNaNValues'; // name spaces import * as planar from './planar'; @@ -72,4 +73,5 @@ export { calculateViewportsSpatialRegistration, spatialRegistrationMetadataProvider, getViewportImageCornersInWorld, + hasNaNValues, }; diff --git a/packages/tools/src/tools/annotation/BidirectionalTool.ts b/packages/tools/src/tools/annotation/BidirectionalTool.ts index 5705a0b5d..bd39bef4c 100644 --- a/packages/tools/src/tools/annotation/BidirectionalTool.ts +++ b/packages/tools/src/tools/annotation/BidirectionalTool.ts @@ -1,4 +1,4 @@ -import { vec2, vec3 } from 'gl-matrix'; +import { vec2, vec3, mat2, mat3, mat2d } from 'gl-matrix'; import { getEnabledElement, triggerEvent, @@ -542,7 +542,8 @@ class BidirectionalTool extends AnnotationTool { }; // ~~ calculate worldPos of our short axis handles - // 1/3 distance between long points + // short axis is perpendicular to long axis, and we set its length to be 2/3 of long axis + // (meaning each) const dist = vec2.distance(canvasCoordPoints[0], canvasCoordPoints[1]); const shortAxisDistFromCenter = dist / 3; @@ -618,7 +619,7 @@ class BidirectionalTool extends AnnotationTool { }); annotation.invalidated = true; } else { - this._mouseDragModifyHandle(evt); + this._handleDragModify(evt); annotation.invalidated = true; } @@ -629,12 +630,12 @@ class BidirectionalTool extends AnnotationTool { * Mouse dragging a handle callback * @param evt - mouse drag event */ - _mouseDragModifyHandle = (evt) => { + _handleDragModify = (evt) => { const eventDetail = evt.detail; const { currentPoints, element } = eventDetail; const enabledElement = getEnabledElement(element); const { viewport } = enabledElement; - const { annotation, handleIndex } = this.editData; + const { annotation, handleIndex: movingHandleIndex } = this.editData; const { data } = annotation; // Moving handle @@ -645,7 +646,7 @@ class BidirectionalTool extends AnnotationTool { viewport.worldToCanvas(data.handles.points[2]), viewport.worldToCanvas(data.handles.points[3]), ]; - // Which line is long? Which line is short? + const firstLineSegment = { start: { x: canvasCoordHandlesCurrent[0][0], @@ -671,16 +672,38 @@ class BidirectionalTool extends AnnotationTool { const proposedPoint = [...worldPos]; const proposedCanvasCoord = viewport.worldToCanvas(proposedPoint); - if (handleIndex === 0 || handleIndex === 1) { - const fixedHandleIndex = handleIndex === 0 ? 1 : 0; + if (movingHandleIndex === 0 || movingHandleIndex === 1) { + const fixedHandleIndex = movingHandleIndex === 0 ? 1 : 0; + + const fixedHandleCanvasCoord = + canvasCoordHandlesCurrent[fixedHandleIndex]; - const fixedCanvasCoord = canvasCoordHandlesCurrent[fixedHandleIndex]; + const fixedHandleToProposedCoordVec = vec2.set( + vec2.create(), + proposedCanvasCoord[0] - fixedHandleCanvasCoord[0], + proposedCanvasCoord[1] - fixedHandleCanvasCoord[1] + ); + + const fixedHandleToOldCoordVec = vec2.set( + vec2.create(), + canvasCoordHandlesCurrent[movingHandleIndex][0] - + fixedHandleCanvasCoord[0], + canvasCoordHandlesCurrent[movingHandleIndex][1] - + fixedHandleCanvasCoord[1] + ); + + // normalize vector + vec2.normalize( + fixedHandleToProposedCoordVec, + fixedHandleToProposedCoordVec + ); + vec2.normalize(fixedHandleToOldCoordVec, fixedHandleToOldCoordVec); // Check whether this const proposedFirstLineSegment = { start: { - x: fixedCanvasCoord[0], - y: fixedCanvasCoord[1], + x: fixedHandleCanvasCoord[0], + y: fixedHandleCanvasCoord[1], }, end: { x: proposedCanvasCoord[0], @@ -688,6 +711,11 @@ class BidirectionalTool extends AnnotationTool { }, }; + // Note: this is the case when we are modifying the long axis line segment + // and we make it shorter and shorter until its second half size becomes zero + // which basically means that any more modification would make the long axis + // second half disappear. In this case, we just bail out and do not update + // since we don't want to disrupt the bidirectional shape. if ( this._movingLongAxisWouldPutItThroughShortAxis( proposedFirstLineSegment, @@ -697,73 +725,62 @@ class BidirectionalTool extends AnnotationTool { return; } - // --> We need to preserve this distance - const intersectionPoint = lineSegment.intersectLine( - [secondLineSegment.start.x, secondLineSegment.start.y], - [secondLineSegment.end.x, secondLineSegment.end.y], - [firstLineSegment.start.x, firstLineSegment.start.y], - [firstLineSegment.end.x, firstLineSegment.end.y] + const centerOfRotation = fixedHandleCanvasCoord; + + const angle = this._getSignedAngle( + fixedHandleToOldCoordVec, + fixedHandleToProposedCoordVec ); - const intersectionCoord = vec2.create(); + // rotate handles around the center of rotation, first translate to origin, + // then rotate, then translate back + let firstPointX = canvasCoordHandlesCurrent[2][0]; + let firstPointY = canvasCoordHandlesCurrent[2][1]; - vec2.set(intersectionCoord, intersectionPoint[0], intersectionPoint[1]); + let secondPointX = canvasCoordHandlesCurrent[3][0]; + let secondPointY = canvasCoordHandlesCurrent[3][1]; - // 1. distance from intersection point to start handle? - const distFromLeftHandle = vec2.distance( - canvasCoordHandlesCurrent[2], - intersectionCoord - ); + // translate to origin + firstPointX -= centerOfRotation[0]; + firstPointY -= centerOfRotation[1]; - // 2. distance from intersection point to end handle? - const distFromRightHandle = vec2.distance( - canvasCoordHandlesCurrent[3], - intersectionCoord - ); + secondPointX -= centerOfRotation[0]; + secondPointY -= centerOfRotation[1]; - // 3. distance from long's opposite handle and intersect point - // Need new intersect x/y - const distIntersectAndFixedPoint = Math.abs( - vec2.distance(fixedCanvasCoord, intersectionCoord) - ); + // rotate + const rotatedFirstPoint = + firstPointX * Math.cos(angle) - firstPointY * Math.sin(angle); + const rotatedFirstPointY = + firstPointX * Math.sin(angle) + firstPointY * Math.cos(angle); - // Find inclination of perpindicular - // Should use proposed point to find new inclination - const dx = fixedCanvasCoord[0] - proposedCanvasCoord[0]; - const dy = fixedCanvasCoord[1] - proposedCanvasCoord[1]; - const length = Math.sqrt(dx * dx + dy * dy); - const vectorX = dx / length; - const vectorY = dy / length; - - // Find new intersection point - // --> fixedPoint, magnitude in perpendicular - // minus if right - // add if left - const intersectX = - fixedCanvasCoord[0] - distIntersectAndFixedPoint * vectorX; - const intersectY = - fixedCanvasCoord[1] - distIntersectAndFixedPoint * vectorY; - - // short points 1/4 distance from center of long points - // Flip signs depending on grabbed handle - const mod = handleIndex === 0 ? -1 : 1; - const leftX = intersectX + distFromLeftHandle * vectorY * mod; - const leftY = intersectY - distFromLeftHandle * vectorX * mod; - const rightX = intersectX - distFromRightHandle * vectorY * mod; - const rightY = intersectY + distFromRightHandle * vectorX * mod; - - data.handles.points[handleIndex] = proposedPoint; - data.handles.points[2] = viewport.canvasToWorld([leftX, leftY]); - data.handles.points[3] = viewport.canvasToWorld([rightX, rightY]); + const rotatedSecondPoint = + secondPointX * Math.cos(angle) - secondPointY * Math.sin(angle); + const rotatedSecondPointY = + secondPointX * Math.sin(angle) + secondPointY * Math.cos(angle); + + // translate back + firstPointX = rotatedFirstPoint + centerOfRotation[0]; + firstPointY = rotatedFirstPointY + centerOfRotation[1]; + + secondPointX = rotatedSecondPoint + centerOfRotation[0]; + secondPointY = rotatedSecondPointY + centerOfRotation[1]; + + // update handles + const newFirstPoint = viewport.canvasToWorld([firstPointX, firstPointY]); + const newSecondPoint = viewport.canvasToWorld([ + secondPointX, + secondPointY, + ]); + + // the fixed handle is the one that is not being moved so we + // don't need to update it + data.handles.points[movingHandleIndex] = proposedPoint; + data.handles.points[2] = newFirstPoint; + data.handles.points[3] = newSecondPoint; } else { // Translation manipulator - const translateHandleIndex = handleIndex === 2 ? 3 : 2; + const translateHandleIndex = movingHandleIndex === 2 ? 3 : 2; - // does not rotate, but can translate entire line (other end of short) - const proposedCanvasCoordPoint = { - x: proposedCanvasCoord[0], - y: proposedCanvasCoord[1], - }; const canvasCoordsCurrent = { longLineSegment: { start: firstLineSegment.start, @@ -775,75 +792,95 @@ class BidirectionalTool extends AnnotationTool { }, }; - // get incline of other line (should not change w/ this movement) - const dx = - canvasCoordsCurrent.longLineSegment.start.x - - canvasCoordsCurrent.longLineSegment.end.x; - const dy = - canvasCoordsCurrent.longLineSegment.start.y - - canvasCoordsCurrent.longLineSegment.end.y; - const length = Math.sqrt(dx * dx + dy * dy); - const vectorX = dx / length; - const vectorY = dy / length; - // Create a helper line to find the intesection point in the long line - const highNumber = Number.MAX_SAFE_INTEGER; - // Get the multiplier - // +1 or -1 depending on which perp end we grabbed (and if it was "fixed" end) - const mod = handleIndex === 0 || handleIndex === 3 ? 1 : -1; - const multiplier = mod * highNumber; - const helperLine = { - start: proposedCanvasCoordPoint, // could be start or end - end: { - x: proposedCanvasCoordPoint.x + vectorY * multiplier, - y: proposedCanvasCoordPoint.y + vectorX * multiplier * -1, - }, - }; - - const newIntersectionPoint = lineSegment.intersectLine( + const longLineSegmentVec = vec2.subtract( + vec2.create(), + [ + canvasCoordsCurrent.longLineSegment.end.x, + canvasCoordsCurrent.longLineSegment.end.y, + ], [ canvasCoordsCurrent.longLineSegment.start.x, canvasCoordsCurrent.longLineSegment.start.y, - ], + ] + ); + + const longLineSegmentVecNormalized = vec2.normalize( + vec2.create(), + longLineSegmentVec + ); + + const proposedToCurrentVec = vec2.subtract( + vec2.create(), + [proposedCanvasCoord[0], proposedCanvasCoord[1]], [ - canvasCoordsCurrent.longLineSegment.end.x, - canvasCoordsCurrent.longLineSegment.end.y, + canvasCoordHandlesCurrent[movingHandleIndex][0], + canvasCoordHandlesCurrent[movingHandleIndex][1], + ] + ); + + const movementLength = vec2.length(proposedToCurrentVec); + + const angle = this._getSignedAngle( + longLineSegmentVecNormalized, + proposedToCurrentVec + ); + + const movementAlongLineSegmentLength = Math.cos(angle) * movementLength; + + const newTranslatedPoint = vec2.scaleAndAdd( + vec2.create(), + [ + canvasCoordHandlesCurrent[translateHandleIndex][0], + canvasCoordHandlesCurrent[translateHandleIndex][1], ], - [helperLine.start.x, helperLine.start.y], - [helperLine.end.x, helperLine.end.y] + longLineSegmentVecNormalized, + movementAlongLineSegmentLength ); - // short-circuit - if (newIntersectionPoint === undefined) { + // don't update if it passes through the other line segment + if ( + this._movingLongAxisWouldPutItThroughShortAxis( + { + start: { + x: proposedCanvasCoord[0], + y: proposedCanvasCoord[1], + }, + end: { + x: newTranslatedPoint[0], + y: newTranslatedPoint[1], + }, + }, + { + start: { + x: canvasCoordsCurrent.longLineSegment.start.x, + y: canvasCoordsCurrent.longLineSegment.start.y, + }, + end: { + x: canvasCoordsCurrent.longLineSegment.end.x, + y: canvasCoordsCurrent.longLineSegment.end.y, + }, + } + ) + ) { return; } - // 1. distance from intersection point to start handle? - const distFromTranslateHandle = vec2.distance( - canvasCoordHandlesCurrent[translateHandleIndex], - [newIntersectionPoint[0], newIntersectionPoint[1]] + const intersectionPoint = lineSegment.intersectLine( + [proposedCanvasCoord[0], proposedCanvasCoord[1]], + [newTranslatedPoint[0], newTranslatedPoint[1]], + [firstLineSegment.start.x, firstLineSegment.start.y], + [firstLineSegment.end.x, firstLineSegment.end.y] ); - // isStart if index is 0 or 2 - const shortLineSegment = { - start: { - x: newIntersectionPoint[0] + vectorY * distFromTranslateHandle, - y: newIntersectionPoint[1] + vectorX * distFromTranslateHandle * -1, - }, - end: { - x: newIntersectionPoint[0] + vectorY * distFromTranslateHandle * -1, - y: newIntersectionPoint[1] + vectorX * distFromTranslateHandle, - }, - }; - const translatedHandleCoords = - translateHandleIndex === 2 - ? shortLineSegment.start - : shortLineSegment.end; - - data.handles.points[translateHandleIndex] = viewport.canvasToWorld([ - translatedHandleCoords.x, - translatedHandleCoords.y, - ]); - data.handles.points[handleIndex] = proposedPoint; + // don't update if it doesn't intersect + if (!intersectionPoint) { + return; + } + + data.handles.points[translateHandleIndex] = viewport.canvasToWorld( + newTranslatedPoint as Types.Point2 + ); + data.handles.points[movingHandleIndex] = proposedPoint; } }; @@ -1130,7 +1167,7 @@ class BidirectionalTool extends AnnotationTool { }; _movingLongAxisWouldPutItThroughShortAxis = ( - proposedFirstLineSegment, + firstLineSegment, secondLineSegment ) => { const vectorInSecondLineDirection = vec2.create(); @@ -1160,8 +1197,8 @@ class BidirectionalTool extends AnnotationTool { const proposedIntersectionPoint = lineSegment.intersectLine( [extendedSecondLineSegment.start.x, extendedSecondLineSegment.start.y], [extendedSecondLineSegment.end.x, extendedSecondLineSegment.end.y], - [proposedFirstLineSegment.start.x, proposedFirstLineSegment.start.y], - [proposedFirstLineSegment.end.x, proposedFirstLineSegment.end.y] + [firstLineSegment.start.x, firstLineSegment.start.y], + [firstLineSegment.end.x, firstLineSegment.end.y] ); const wouldPutThroughShortAxis = !proposedIntersectionPoint; @@ -1268,6 +1305,13 @@ class BidirectionalTool extends AnnotationTool { csUtils.indexWithinDimensions(index4, dimensions) ); }; + + _getSignedAngle = (vector1, vector2) => { + return Math.atan2( + vector1[0] * vector2[1] - vector1[1] * vector2[0], + vector1[0] * vector2[0] + vector1[1] * vector2[1] + ); + }; } BidirectionalTool.toolName = 'Bidirectional'; diff --git a/utils/ExampleRunner/example-info.json b/utils/ExampleRunner/example-info.json index 9a29d04a9..551b5399b 100644 --- a/utils/ExampleRunner/example-info.json +++ b/utils/ExampleRunner/example-info.json @@ -90,7 +90,7 @@ "name": "DICOM P10 from the local file system", "description": "Provides an interface to load a DICOM P10 image from your local file system to the Cornerstone3D" }, - "local (CPU)": { + "localCPU": { "name": "DICOM P10 from the local file system using CPU", "description": "Cornerstone3D uses WebGL for rendering by default (if available) and a fallback to CPU. This example force rendering on CPU for debugging purposes." }, From 0dc8537143adea05ee65626059934327090e655f Mon Sep 17 00:00:00 2001 From: ohif-bot Date: Thu, 1 Dec 2022 13:27:45 +0000 Subject: [PATCH 18/25] chore(release): publish [skip ci] - @cornerstonejs/core@0.22.2 - docs@0.7.15 - @cornerstonejs/streaming-image-volume-loader@0.6.10 - @cornerstonejs/tools@0.30.5 --- packages/core/CHANGELOG.md | 11 +++++++++++ packages/core/package.json | 2 +- packages/docs/CHANGELOG.md | 8 ++++++++ packages/docs/package.json | 8 ++++---- packages/streaming-image-volume-loader/CHANGELOG.md | 8 ++++++++ packages/streaming-image-volume-loader/package.json | 4 ++-- packages/tools/CHANGELOG.md | 11 +++++++++++ packages/tools/package.json | 4 ++-- 8 files changed, 47 insertions(+), 9 deletions(-) diff --git a/packages/core/CHANGELOG.md b/packages/core/CHANGELOG.md index dd3e710a8..7b52da063 100644 --- a/packages/core/CHANGELOG.md +++ b/packages/core/CHANGELOG.md @@ -3,6 +3,17 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.22.2](https://github.com/cornerstonejs/cornerstone3D-beta/compare/@cornerstonejs/core@0.22.1...@cornerstonejs/core@0.22.2) (2022-12-01) + + +### Bug Fixes + +* bidirectional tool when short and long axis changes ([#309](https://github.com/cornerstonejs/cornerstone3D-beta/issues/309)) ([f973e72](https://github.com/cornerstonejs/cornerstone3D-beta/commit/f973e7262897a2daf4f37363d3e818ae88620bb8)) + + + + + ## [0.22.1](https://github.com/cornerstonejs/cornerstone3D-beta/compare/@cornerstonejs/core@0.22.0...@cornerstonejs/core@0.22.1) (2022-12-01) diff --git a/packages/core/package.json b/packages/core/package.json index c6f415365..00ae8f9aa 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "@cornerstonejs/core", - "version": "0.22.1", + "version": "0.22.2", "description": "", "main": "dist/umd/index.js", "types": "dist/esm/index.d.ts", diff --git a/packages/docs/CHANGELOG.md b/packages/docs/CHANGELOG.md index 318a2e40b..6b92f4027 100644 --- a/packages/docs/CHANGELOG.md +++ b/packages/docs/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.7.15](https://github.com/cornerstonejs/cornerstone3D-beta/compare/docs@0.7.14...docs@0.7.15) (2022-12-01) + +**Note:** Version bump only for package docs + + + + + ## [0.7.14](https://github.com/cornerstonejs/cornerstone3D-beta/compare/docs@0.7.13...docs@0.7.14) (2022-12-01) **Note:** Version bump only for package docs diff --git a/packages/docs/package.json b/packages/docs/package.json index a110c47ef..5a51c6662 100644 --- a/packages/docs/package.json +++ b/packages/docs/package.json @@ -1,6 +1,6 @@ { "name": "docs", - "version": "0.7.14", + "version": "0.7.15", "private": true, "repository": "https://github.com/cornerstonejs/cornerstone3D-beta", "scripts": { @@ -28,9 +28,9 @@ "write-heading-ids": "docusaurus write-heading-ids" }, "dependencies": { - "@cornerstonejs/core": "^0.22.1", - "@cornerstonejs/streaming-image-volume-loader": "^0.6.9", - "@cornerstonejs/tools": "^0.30.4", + "@cornerstonejs/core": "^0.22.2", + "@cornerstonejs/streaming-image-volume-loader": "^0.6.10", + "@cornerstonejs/tools": "^0.30.5", "@docusaurus/core": "2.0.0-rc.1", "@docusaurus/module-type-aliases": "2.0.0-rc.1", "@docusaurus/preset-classic": "2.0.0-rc.1", diff --git a/packages/streaming-image-volume-loader/CHANGELOG.md b/packages/streaming-image-volume-loader/CHANGELOG.md index e80c5ee93..1ad11be3f 100644 --- a/packages/streaming-image-volume-loader/CHANGELOG.md +++ b/packages/streaming-image-volume-loader/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.6.10](https://github.com/cornerstonejs/cornerstone3D-beta/compare/@cornerstonejs/streaming-image-volume-loader@0.6.9...@cornerstonejs/streaming-image-volume-loader@0.6.10) (2022-12-01) + +**Note:** Version bump only for package @cornerstonejs/streaming-image-volume-loader + + + + + ## [0.6.9](https://github.com/cornerstonejs/cornerstone3D-beta/compare/@cornerstonejs/streaming-image-volume-loader@0.6.8...@cornerstonejs/streaming-image-volume-loader@0.6.9) (2022-12-01) **Note:** Version bump only for package @cornerstonejs/streaming-image-volume-loader diff --git a/packages/streaming-image-volume-loader/package.json b/packages/streaming-image-volume-loader/package.json index 3b4f9dc12..0f263b956 100644 --- a/packages/streaming-image-volume-loader/package.json +++ b/packages/streaming-image-volume-loader/package.json @@ -1,6 +1,6 @@ { "name": "@cornerstonejs/streaming-image-volume-loader", - "version": "0.6.9", + "version": "0.6.10", "description": "", "main": "dist/umd/index.js", "types": "dist/esm/index.d.ts", @@ -26,7 +26,7 @@ "webpack:watch": "webpack --mode development --progress --watch --config ./.webpack/webpack.dev.js" }, "dependencies": { - "@cornerstonejs/core": "^0.22.1", + "@cornerstonejs/core": "^0.22.2", "cornerstone-wado-image-loader": "^4.2.1" }, "peerDependencies": { diff --git a/packages/tools/CHANGELOG.md b/packages/tools/CHANGELOG.md index d3675e909..ecf444e23 100644 --- a/packages/tools/CHANGELOG.md +++ b/packages/tools/CHANGELOG.md @@ -3,6 +3,17 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.30.5](https://github.com/cornerstonejs/cornerstone3D-beta/compare/@cornerstonejs/tools@0.30.4...@cornerstonejs/tools@0.30.5) (2022-12-01) + + +### Bug Fixes + +* bidirectional tool when short and long axis changes ([#309](https://github.com/cornerstonejs/cornerstone3D-beta/issues/309)) ([f973e72](https://github.com/cornerstonejs/cornerstone3D-beta/commit/f973e7262897a2daf4f37363d3e818ae88620bb8)) + + + + + ## [0.30.4](https://github.com/cornerstonejs/cornerstone3D-beta/compare/@cornerstonejs/tools@0.30.3...@cornerstonejs/tools@0.30.4) (2022-12-01) diff --git a/packages/tools/package.json b/packages/tools/package.json index d855d0c9a..593add400 100644 --- a/packages/tools/package.json +++ b/packages/tools/package.json @@ -1,6 +1,6 @@ { "name": "@cornerstonejs/tools", - "version": "0.30.4", + "version": "0.30.5", "description": "Cornerstone3D Tools", "main": "dist/umd/index.js", "types": "dist/esm/index.d.ts", @@ -26,7 +26,7 @@ "webpack:watch": "webpack --mode development --progress --watch --config ./.webpack/webpack.dev.js" }, "dependencies": { - "@cornerstonejs/core": "^0.22.1", + "@cornerstonejs/core": "^0.22.2", "lodash.clonedeep": "4.5.0", "lodash.get": "^4.4.2" }, From a58a831f4c5ab4c9ea9d7d258a59cbdcb5c837e4 Mon Sep 17 00:00:00 2001 From: Gabriel Lebaudy Date: Thu, 1 Dec 2022 15:53:55 +0100 Subject: [PATCH 19/25] fix(volumeViewport): Add optional scaling as the volume can be undefined (#323) While trying to get the volume from the cache, it can be undefined so getting the scaling attribute would throw an error in that case. This is a quick fix --- packages/core/src/RenderingEngine/VolumeViewport.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/src/RenderingEngine/VolumeViewport.ts b/packages/core/src/RenderingEngine/VolumeViewport.ts index d063bea8a..112f1dc73 100644 --- a/packages/core/src/RenderingEngine/VolumeViewport.ts +++ b/packages/core/src/RenderingEngine/VolumeViewport.ts @@ -751,7 +751,7 @@ class VolumeViewport extends Viewport implements IVolumeViewport { metadata: { Modality: volume?.metadata?.Modality, }, - scaling: volume.scaling, + scaling: volume?.scaling, hasPixelSpacing: true, }; } From 6ef505cf2c442115ba52e81458f1fd59a9fdf91e Mon Sep 17 00:00:00 2001 From: ohif-bot Date: Thu, 1 Dec 2022 14:58:35 +0000 Subject: [PATCH 20/25] chore(release): publish [skip ci] - @cornerstonejs/core@0.22.3 - docs@0.7.16 - @cornerstonejs/streaming-image-volume-loader@0.6.11 - @cornerstonejs/tools@0.30.6 --- packages/core/CHANGELOG.md | 11 +++++++++++ packages/core/package.json | 2 +- packages/docs/CHANGELOG.md | 8 ++++++++ packages/docs/package.json | 8 ++++---- packages/streaming-image-volume-loader/CHANGELOG.md | 8 ++++++++ packages/streaming-image-volume-loader/package.json | 4 ++-- packages/tools/CHANGELOG.md | 8 ++++++++ packages/tools/package.json | 4 ++-- 8 files changed, 44 insertions(+), 9 deletions(-) diff --git a/packages/core/CHANGELOG.md b/packages/core/CHANGELOG.md index 7b52da063..b9825ea05 100644 --- a/packages/core/CHANGELOG.md +++ b/packages/core/CHANGELOG.md @@ -3,6 +3,17 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.22.3](https://github.com/cornerstonejs/cornerstone3D-beta/compare/@cornerstonejs/core@0.22.2...@cornerstonejs/core@0.22.3) (2022-12-01) + + +### Bug Fixes + +* **volumeViewport:** Add optional scaling as the volume can be undefined ([#323](https://github.com/cornerstonejs/cornerstone3D-beta/issues/323)) ([a58a831](https://github.com/cornerstonejs/cornerstone3D-beta/commit/a58a831f4c5ab4c9ea9d7d258a59cbdcb5c837e4)) + + + + + ## [0.22.2](https://github.com/cornerstonejs/cornerstone3D-beta/compare/@cornerstonejs/core@0.22.1...@cornerstonejs/core@0.22.2) (2022-12-01) diff --git a/packages/core/package.json b/packages/core/package.json index 00ae8f9aa..cb571427d 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "@cornerstonejs/core", - "version": "0.22.2", + "version": "0.22.3", "description": "", "main": "dist/umd/index.js", "types": "dist/esm/index.d.ts", diff --git a/packages/docs/CHANGELOG.md b/packages/docs/CHANGELOG.md index 6b92f4027..27ecd26a3 100644 --- a/packages/docs/CHANGELOG.md +++ b/packages/docs/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.7.16](https://github.com/cornerstonejs/cornerstone3D-beta/compare/docs@0.7.15...docs@0.7.16) (2022-12-01) + +**Note:** Version bump only for package docs + + + + + ## [0.7.15](https://github.com/cornerstonejs/cornerstone3D-beta/compare/docs@0.7.14...docs@0.7.15) (2022-12-01) **Note:** Version bump only for package docs diff --git a/packages/docs/package.json b/packages/docs/package.json index 5a51c6662..d66bd0c27 100644 --- a/packages/docs/package.json +++ b/packages/docs/package.json @@ -1,6 +1,6 @@ { "name": "docs", - "version": "0.7.15", + "version": "0.7.16", "private": true, "repository": "https://github.com/cornerstonejs/cornerstone3D-beta", "scripts": { @@ -28,9 +28,9 @@ "write-heading-ids": "docusaurus write-heading-ids" }, "dependencies": { - "@cornerstonejs/core": "^0.22.2", - "@cornerstonejs/streaming-image-volume-loader": "^0.6.10", - "@cornerstonejs/tools": "^0.30.5", + "@cornerstonejs/core": "^0.22.3", + "@cornerstonejs/streaming-image-volume-loader": "^0.6.11", + "@cornerstonejs/tools": "^0.30.6", "@docusaurus/core": "2.0.0-rc.1", "@docusaurus/module-type-aliases": "2.0.0-rc.1", "@docusaurus/preset-classic": "2.0.0-rc.1", diff --git a/packages/streaming-image-volume-loader/CHANGELOG.md b/packages/streaming-image-volume-loader/CHANGELOG.md index 1ad11be3f..76b6f06cc 100644 --- a/packages/streaming-image-volume-loader/CHANGELOG.md +++ b/packages/streaming-image-volume-loader/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.6.11](https://github.com/cornerstonejs/cornerstone3D-beta/compare/@cornerstonejs/streaming-image-volume-loader@0.6.10...@cornerstonejs/streaming-image-volume-loader@0.6.11) (2022-12-01) + +**Note:** Version bump only for package @cornerstonejs/streaming-image-volume-loader + + + + + ## [0.6.10](https://github.com/cornerstonejs/cornerstone3D-beta/compare/@cornerstonejs/streaming-image-volume-loader@0.6.9...@cornerstonejs/streaming-image-volume-loader@0.6.10) (2022-12-01) **Note:** Version bump only for package @cornerstonejs/streaming-image-volume-loader diff --git a/packages/streaming-image-volume-loader/package.json b/packages/streaming-image-volume-loader/package.json index 0f263b956..0d49f37b8 100644 --- a/packages/streaming-image-volume-loader/package.json +++ b/packages/streaming-image-volume-loader/package.json @@ -1,6 +1,6 @@ { "name": "@cornerstonejs/streaming-image-volume-loader", - "version": "0.6.10", + "version": "0.6.11", "description": "", "main": "dist/umd/index.js", "types": "dist/esm/index.d.ts", @@ -26,7 +26,7 @@ "webpack:watch": "webpack --mode development --progress --watch --config ./.webpack/webpack.dev.js" }, "dependencies": { - "@cornerstonejs/core": "^0.22.2", + "@cornerstonejs/core": "^0.22.3", "cornerstone-wado-image-loader": "^4.2.1" }, "peerDependencies": { diff --git a/packages/tools/CHANGELOG.md b/packages/tools/CHANGELOG.md index ecf444e23..9aeb8190c 100644 --- a/packages/tools/CHANGELOG.md +++ b/packages/tools/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.30.6](https://github.com/cornerstonejs/cornerstone3D-beta/compare/@cornerstonejs/tools@0.30.5...@cornerstonejs/tools@0.30.6) (2022-12-01) + +**Note:** Version bump only for package @cornerstonejs/tools + + + + + ## [0.30.5](https://github.com/cornerstonejs/cornerstone3D-beta/compare/@cornerstonejs/tools@0.30.4...@cornerstonejs/tools@0.30.5) (2022-12-01) diff --git a/packages/tools/package.json b/packages/tools/package.json index 593add400..fd79f14f3 100644 --- a/packages/tools/package.json +++ b/packages/tools/package.json @@ -1,6 +1,6 @@ { "name": "@cornerstonejs/tools", - "version": "0.30.5", + "version": "0.30.6", "description": "Cornerstone3D Tools", "main": "dist/umd/index.js", "types": "dist/esm/index.d.ts", @@ -26,7 +26,7 @@ "webpack:watch": "webpack --mode development --progress --watch --config ./.webpack/webpack.dev.js" }, "dependencies": { - "@cornerstonejs/core": "^0.22.2", + "@cornerstonejs/core": "^0.22.3", "lodash.clonedeep": "4.5.0", "lodash.get": "^4.4.2" }, From 1dd315c61476f7bca5640033f530bcc956d14307 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mustafa=20=C3=87=C4=B0N=C4=B0?= Date: Thu, 1 Dec 2022 22:26:37 +0300 Subject: [PATCH 21/25] fix: Use queryselector instead of firstChild to get svg-layer (#268) --- packages/tools/src/drawingSvg/getSvgDrawingHelper.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/tools/src/drawingSvg/getSvgDrawingHelper.ts b/packages/tools/src/drawingSvg/getSvgDrawingHelper.ts index 358102de8..baed7faee 100644 --- a/packages/tools/src/drawingSvg/getSvgDrawingHelper.ts +++ b/packages/tools/src/drawingSvg/getSvgDrawingHelper.ts @@ -2,6 +2,8 @@ import { state } from '../store'; import { getEnabledElement } from '@cornerstonejs/core'; import { SVGDrawingHelper } from '../types'; +const VIEWPORT_ELEMENT = 'viewport-element'; + /** * Returns the SVG drawing helper for the given HTML element. * @param element - The HTML element to get the SVG drawing helper for. @@ -34,7 +36,8 @@ function getSvgDrawingHelper(element: HTMLDivElement): SVGDrawingHelper { * @private */ function _getSvgLayer(element) { - const internalDivElement = element.firstChild; + const viewportElement = `.${VIEWPORT_ELEMENT}`; + const internalDivElement = element.querySelector(viewportElement); const svgLayer = internalDivElement.querySelector('.svg-layer'); return svgLayer; From f9de5194db3362b1d900372740132a18f5e56236 Mon Sep 17 00:00:00 2001 From: ohif-bot Date: Thu, 1 Dec 2022 19:30:58 +0000 Subject: [PATCH 22/25] chore(release): publish [skip ci] - docs@0.7.17 - @cornerstonejs/tools@0.30.7 --- packages/docs/CHANGELOG.md | 8 ++++++++ packages/docs/package.json | 4 ++-- packages/tools/CHANGELOG.md | 11 +++++++++++ packages/tools/package.json | 2 +- 4 files changed, 22 insertions(+), 3 deletions(-) diff --git a/packages/docs/CHANGELOG.md b/packages/docs/CHANGELOG.md index 27ecd26a3..dadad1627 100644 --- a/packages/docs/CHANGELOG.md +++ b/packages/docs/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.7.17](https://github.com/cornerstonejs/cornerstone3D-beta/compare/docs@0.7.16...docs@0.7.17) (2022-12-01) + +**Note:** Version bump only for package docs + + + + + ## [0.7.16](https://github.com/cornerstonejs/cornerstone3D-beta/compare/docs@0.7.15...docs@0.7.16) (2022-12-01) **Note:** Version bump only for package docs diff --git a/packages/docs/package.json b/packages/docs/package.json index d66bd0c27..9a3640227 100644 --- a/packages/docs/package.json +++ b/packages/docs/package.json @@ -1,6 +1,6 @@ { "name": "docs", - "version": "0.7.16", + "version": "0.7.17", "private": true, "repository": "https://github.com/cornerstonejs/cornerstone3D-beta", "scripts": { @@ -30,7 +30,7 @@ "dependencies": { "@cornerstonejs/core": "^0.22.3", "@cornerstonejs/streaming-image-volume-loader": "^0.6.11", - "@cornerstonejs/tools": "^0.30.6", + "@cornerstonejs/tools": "^0.30.7", "@docusaurus/core": "2.0.0-rc.1", "@docusaurus/module-type-aliases": "2.0.0-rc.1", "@docusaurus/preset-classic": "2.0.0-rc.1", diff --git a/packages/tools/CHANGELOG.md b/packages/tools/CHANGELOG.md index 9aeb8190c..1294b54f1 100644 --- a/packages/tools/CHANGELOG.md +++ b/packages/tools/CHANGELOG.md @@ -3,6 +3,17 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.30.7](https://github.com/cornerstonejs/cornerstone3D-beta/compare/@cornerstonejs/tools@0.30.6...@cornerstonejs/tools@0.30.7) (2022-12-01) + + +### Bug Fixes + +* Use queryselector instead of firstChild to get svg-layer ([#268](https://github.com/cornerstonejs/cornerstone3D-beta/issues/268)) ([1dd315c](https://github.com/cornerstonejs/cornerstone3D-beta/commit/1dd315c61476f7bca5640033f530bcc956d14307)) + + + + + ## [0.30.6](https://github.com/cornerstonejs/cornerstone3D-beta/compare/@cornerstonejs/tools@0.30.5...@cornerstonejs/tools@0.30.6) (2022-12-01) **Note:** Version bump only for package @cornerstonejs/tools diff --git a/packages/tools/package.json b/packages/tools/package.json index fd79f14f3..ae79b2292 100644 --- a/packages/tools/package.json +++ b/packages/tools/package.json @@ -1,6 +1,6 @@ { "name": "@cornerstonejs/tools", - "version": "0.30.6", + "version": "0.30.7", "description": "Cornerstone3D Tools", "main": "dist/umd/index.js", "types": "dist/esm/index.d.ts", From 82da3f0c6d6e3752eb7249372081fae164072f1f Mon Sep 17 00:00:00 2001 From: James Manners Date: Wed, 7 Dec 2022 14:01:04 +1100 Subject: [PATCH 23/25] [wip] initial dicom-loader typescript conversion --- .../.webpack/webpack-base.js | 9 ++++---- .../.webpack/webpack-bundle.js | 11 +++++----- .../.webpack/webpack-esm.js | 5 +++-- ...{externalModules.js => externalModules.ts} | 22 ++++++++++--------- ...PALETTECOLOR.js => convertPALETTECOLOR.ts} | 0 ...orByPixel.js => convertRGBColorByPixel.ts} | 0 ...orByPlane.js => convertRGBColorByPlane.ts} | 0 ...ByPixel.js => convertYBRFull422ByPixel.ts} | 0 ...ullByPixel.js => convertYBRFullByPixel.ts} | 0 ...ullByPlane.js => convertYBRFullByPlane.ts} | 0 .../{index.js => index.ts} | 0 .../{configure.js => configure.ts} | 0 ...vertColorSpace.js => convertColorSpace.ts} | 0 .../{createImage.js => createImage.ts} | 0 ...rkers.js => decodeImageFrame-noWorkers.ts} | 0 ...ecodeImageFrame.js => decodeImageFrame.ts} | 0 ...olor.js => decodeJPEGBaseline8BitColor.ts} | 0 .../{getImageFrame.js => getImageFrame.ts} | 0 .../{getMinMax.js => getMinMax.ts} | 0 ...gParameters.js => getScalingParameters.ts} | 0 .../{imageIdToURI.js => imageIdToURI.ts} | 0 ...{index-noWorkers.js => index-noWorkers.ts} | 0 .../src/imageLoader/{index.js => index.ts} | 0 .../internal/{index.js => index.ts} | 0 .../internal/{options.js => options.ts} | 0 .../internal/{xhrRequest.js => xhrRequest.ts} | 0 .../{isColorImage.js => isColorImage.ts} | 0 ...BitColor.js => isJPEGBaseline8BitColor.ts} | 0 ...{registerLoaders.js => registerLoaders.ts} | 0 ...dIndexOfString.js => findIndexOfString.ts} | 0 .../{getPixelData.js => getPixelData.ts} | 0 .../imageLoader/wadors/{index.js => index.ts} | 0 .../wadors/{loadImage.js => loadImage.ts} | 0 .../{loadImage_test.js => loadImage_test.ts} | 0 ...{getNumberString.js => getNumberString.ts} | 0 .../{getNumberValue.js => getNumberValue.ts} | 0 ...{getNumberValues.js => getNumberValues.ts} | 0 ...laneModule.js => getOverlayPlaneModule.ts} | 0 .../metaData/{getValue.js => getValue.ts} | 0 .../wadors/metaData/{index.js => index.ts} | 0 ...etaDataProvider.js => metaDataProvider.ts} | 0 ...{metaDataManager.js => metaDataManager.ts} | 0 .../wadors/{register.js => register.ts} | 0 ...CacheManager.js => dataSetCacheManager.ts} | 0 ...er_test.js => dataSetCacheManager_test.ts} | 0 .../{fileManager.js => fileManager.ts} | 0 ...eFrame.js => getEncapsulatedImageFrame.ts} | 0 .../{getPixelData.js => getPixelData.ts} | 0 ...eFrame.js => getUncompressedImageFrame.ts} | 0 .../wadouri/{index.js => index.ts} | 0 ...{loadFileRequest.js => loadFileRequest.ts} | 0 .../wadouri/{loadImage.js => loadImage.ts} | 0 ...ePixelModule.js => getImagePixelModule.ts} | 0 .../metaData/{getLUTs.js => getLUTs.ts} | 0 ...etModalityLUTOutputPixelRepresentation.ts} | 0 ...{getNumberValues.js => getNumberValues.ts} | 0 ...laneModule.js => getOverlayPlaneModule.ts} | 0 .../wadouri/metaData/{index.js => index.ts} | 0 ...etaDataProvider.js => metaDataProvider.ts} | 0 .../{parseImageId.js => parseImageId.ts} | 0 .../wadouri/{register.js => register.ts} | 0 ...ackBinaryFrame.js => unpackBinaryFrame.ts} | 0 ...ebWorkerManager.js => webWorkerManager.ts} | 0 ...nager_test.js => webWorkerManager_test.ts} | 0 ...{calculateMinMax.js => calculateMinMax.ts} | 0 ...MinMax_test.js => calculateMinMax_test.ts} | 0 ...ecodeImageFrame.js => decodeImageFrame.ts} | 0 ...{decodeBigEndian.js => decodeBigEndian.ts} | 0 .../{decodeJPEG2000.js => decodeJPEG2000.ts} | 0 ...it-js.js => decodeJPEGBaseline12Bit-js.ts} | 0 ...JPEGBaseline12Bit-wasm-not-yet-working.ts} | 0 ...eline8Bit.js => decodeJPEGBaseline8Bit.ts} | 0 .../{decodeJPEGLS.js => decodeJPEGLS.ts} | 0 ...eJPEGLossless.js => decodeJPEGLossless.ts} | 0 ...eLittleEndian.js => decodeLittleEndian.ts} | 0 .../decoders/{decodeRLE.js => decodeRLE.ts} | 0 .../src/shared/{getMinMax.js => getMinMax.ts} | 0 .../{getMinMax_test.js => getMinMax_test.ts} | 0 .../scaling/{scaleArray.js => scaleArray.ts} | 0 .../src/{version.js => version.ts} | 0 .../{decodeTask.js => decodeTask.ts} | 0 .../{index.worker.js => index.worker.ts} | 0 .../webWorker/{webWorker.js => webWorker.ts} | 0 83 files changed, 26 insertions(+), 21 deletions(-) rename packages/dicom-image-loader/src/{externalModules.js => externalModules.ts} (59%) rename packages/dicom-image-loader/src/imageLoader/colorSpaceConverters/{convertPALETTECOLOR.js => convertPALETTECOLOR.ts} (100%) rename packages/dicom-image-loader/src/imageLoader/colorSpaceConverters/{convertRGBColorByPixel.js => convertRGBColorByPixel.ts} (100%) rename packages/dicom-image-loader/src/imageLoader/colorSpaceConverters/{convertRGBColorByPlane.js => convertRGBColorByPlane.ts} (100%) rename packages/dicom-image-loader/src/imageLoader/colorSpaceConverters/{convertYBRFull422ByPixel.js => convertYBRFull422ByPixel.ts} (100%) rename packages/dicom-image-loader/src/imageLoader/colorSpaceConverters/{convertYBRFullByPixel.js => convertYBRFullByPixel.ts} (100%) rename packages/dicom-image-loader/src/imageLoader/colorSpaceConverters/{convertYBRFullByPlane.js => convertYBRFullByPlane.ts} (100%) rename packages/dicom-image-loader/src/imageLoader/colorSpaceConverters/{index.js => index.ts} (100%) rename packages/dicom-image-loader/src/imageLoader/{configure.js => configure.ts} (100%) rename packages/dicom-image-loader/src/imageLoader/{convertColorSpace.js => convertColorSpace.ts} (100%) rename packages/dicom-image-loader/src/imageLoader/{createImage.js => createImage.ts} (100%) rename packages/dicom-image-loader/src/imageLoader/{decodeImageFrame-noWorkers.js => decodeImageFrame-noWorkers.ts} (100%) rename packages/dicom-image-loader/src/imageLoader/{decodeImageFrame.js => decodeImageFrame.ts} (100%) rename packages/dicom-image-loader/src/imageLoader/{decodeJPEGBaseline8BitColor.js => decodeJPEGBaseline8BitColor.ts} (100%) rename packages/dicom-image-loader/src/imageLoader/{getImageFrame.js => getImageFrame.ts} (100%) rename packages/dicom-image-loader/src/imageLoader/{getMinMax.js => getMinMax.ts} (100%) rename packages/dicom-image-loader/src/imageLoader/{getScalingParameters.js => getScalingParameters.ts} (100%) rename packages/dicom-image-loader/src/imageLoader/{imageIdToURI.js => imageIdToURI.ts} (100%) rename packages/dicom-image-loader/src/imageLoader/{index-noWorkers.js => index-noWorkers.ts} (100%) rename packages/dicom-image-loader/src/imageLoader/{index.js => index.ts} (100%) rename packages/dicom-image-loader/src/imageLoader/internal/{index.js => index.ts} (100%) rename packages/dicom-image-loader/src/imageLoader/internal/{options.js => options.ts} (100%) rename packages/dicom-image-loader/src/imageLoader/internal/{xhrRequest.js => xhrRequest.ts} (100%) rename packages/dicom-image-loader/src/imageLoader/{isColorImage.js => isColorImage.ts} (100%) rename packages/dicom-image-loader/src/imageLoader/{isJPEGBaseline8BitColor.js => isJPEGBaseline8BitColor.ts} (100%) rename packages/dicom-image-loader/src/imageLoader/{registerLoaders.js => registerLoaders.ts} (100%) rename packages/dicom-image-loader/src/imageLoader/wadors/{findIndexOfString.js => findIndexOfString.ts} (100%) rename packages/dicom-image-loader/src/imageLoader/wadors/{getPixelData.js => getPixelData.ts} (100%) rename packages/dicom-image-loader/src/imageLoader/wadors/{index.js => index.ts} (100%) rename packages/dicom-image-loader/src/imageLoader/wadors/{loadImage.js => loadImage.ts} (100%) rename packages/dicom-image-loader/src/imageLoader/wadors/{loadImage_test.js => loadImage_test.ts} (100%) rename packages/dicom-image-loader/src/imageLoader/wadors/metaData/{getNumberString.js => getNumberString.ts} (100%) rename packages/dicom-image-loader/src/imageLoader/wadors/metaData/{getNumberValue.js => getNumberValue.ts} (100%) rename packages/dicom-image-loader/src/imageLoader/wadors/metaData/{getNumberValues.js => getNumberValues.ts} (100%) rename packages/dicom-image-loader/src/imageLoader/wadors/metaData/{getOverlayPlaneModule.js => getOverlayPlaneModule.ts} (100%) rename packages/dicom-image-loader/src/imageLoader/wadors/metaData/{getValue.js => getValue.ts} (100%) rename packages/dicom-image-loader/src/imageLoader/wadors/metaData/{index.js => index.ts} (100%) rename packages/dicom-image-loader/src/imageLoader/wadors/metaData/{metaDataProvider.js => metaDataProvider.ts} (100%) rename packages/dicom-image-loader/src/imageLoader/wadors/{metaDataManager.js => metaDataManager.ts} (100%) rename packages/dicom-image-loader/src/imageLoader/wadors/{register.js => register.ts} (100%) rename packages/dicom-image-loader/src/imageLoader/wadouri/{dataSetCacheManager.js => dataSetCacheManager.ts} (100%) rename packages/dicom-image-loader/src/imageLoader/wadouri/{dataSetCacheManager_test.js => dataSetCacheManager_test.ts} (100%) rename packages/dicom-image-loader/src/imageLoader/wadouri/{fileManager.js => fileManager.ts} (100%) rename packages/dicom-image-loader/src/imageLoader/wadouri/{getEncapsulatedImageFrame.js => getEncapsulatedImageFrame.ts} (100%) rename packages/dicom-image-loader/src/imageLoader/wadouri/{getPixelData.js => getPixelData.ts} (100%) rename packages/dicom-image-loader/src/imageLoader/wadouri/{getUncompressedImageFrame.js => getUncompressedImageFrame.ts} (100%) rename packages/dicom-image-loader/src/imageLoader/wadouri/{index.js => index.ts} (100%) rename packages/dicom-image-loader/src/imageLoader/wadouri/{loadFileRequest.js => loadFileRequest.ts} (100%) rename packages/dicom-image-loader/src/imageLoader/wadouri/{loadImage.js => loadImage.ts} (100%) rename packages/dicom-image-loader/src/imageLoader/wadouri/metaData/{getImagePixelModule.js => getImagePixelModule.ts} (100%) rename packages/dicom-image-loader/src/imageLoader/wadouri/metaData/{getLUTs.js => getLUTs.ts} (100%) rename packages/dicom-image-loader/src/imageLoader/wadouri/metaData/{getModalityLUTOutputPixelRepresentation.js => getModalityLUTOutputPixelRepresentation.ts} (100%) rename packages/dicom-image-loader/src/imageLoader/wadouri/metaData/{getNumberValues.js => getNumberValues.ts} (100%) rename packages/dicom-image-loader/src/imageLoader/wadouri/metaData/{getOverlayPlaneModule.js => getOverlayPlaneModule.ts} (100%) rename packages/dicom-image-loader/src/imageLoader/wadouri/metaData/{index.js => index.ts} (100%) rename packages/dicom-image-loader/src/imageLoader/wadouri/metaData/{metaDataProvider.js => metaDataProvider.ts} (100%) rename packages/dicom-image-loader/src/imageLoader/wadouri/{parseImageId.js => parseImageId.ts} (100%) rename packages/dicom-image-loader/src/imageLoader/wadouri/{register.js => register.ts} (100%) rename packages/dicom-image-loader/src/imageLoader/wadouri/{unpackBinaryFrame.js => unpackBinaryFrame.ts} (100%) rename packages/dicom-image-loader/src/imageLoader/{webWorkerManager.js => webWorkerManager.ts} (100%) rename packages/dicom-image-loader/src/imageLoader/{webWorkerManager_test.js => webWorkerManager_test.ts} (100%) rename packages/dicom-image-loader/src/shared/{calculateMinMax.js => calculateMinMax.ts} (100%) rename packages/dicom-image-loader/src/shared/{calculateMinMax_test.js => calculateMinMax_test.ts} (100%) rename packages/dicom-image-loader/src/shared/{decodeImageFrame.js => decodeImageFrame.ts} (100%) rename packages/dicom-image-loader/src/shared/decoders/{decodeBigEndian.js => decodeBigEndian.ts} (100%) rename packages/dicom-image-loader/src/shared/decoders/{decodeJPEG2000.js => decodeJPEG2000.ts} (100%) rename packages/dicom-image-loader/src/shared/decoders/{decodeJPEGBaseline12Bit-js.js => decodeJPEGBaseline12Bit-js.ts} (100%) rename packages/dicom-image-loader/src/shared/decoders/{decodeJPEGBaseline12Bit-wasm-not-yet-working.js => decodeJPEGBaseline12Bit-wasm-not-yet-working.ts} (100%) rename packages/dicom-image-loader/src/shared/decoders/{decodeJPEGBaseline8Bit.js => decodeJPEGBaseline8Bit.ts} (100%) rename packages/dicom-image-loader/src/shared/decoders/{decodeJPEGLS.js => decodeJPEGLS.ts} (100%) rename packages/dicom-image-loader/src/shared/decoders/{decodeJPEGLossless.js => decodeJPEGLossless.ts} (100%) rename packages/dicom-image-loader/src/shared/decoders/{decodeLittleEndian.js => decodeLittleEndian.ts} (100%) rename packages/dicom-image-loader/src/shared/decoders/{decodeRLE.js => decodeRLE.ts} (100%) rename packages/dicom-image-loader/src/shared/{getMinMax.js => getMinMax.ts} (100%) rename packages/dicom-image-loader/src/shared/{getMinMax_test.js => getMinMax_test.ts} (100%) rename packages/dicom-image-loader/src/shared/scaling/{scaleArray.js => scaleArray.ts} (100%) rename packages/dicom-image-loader/src/{version.js => version.ts} (100%) rename packages/dicom-image-loader/src/webWorker/{decodeTask.js => decodeTask.ts} (100%) rename packages/dicom-image-loader/src/webWorker/{index.worker.js => index.worker.ts} (100%) rename packages/dicom-image-loader/src/webWorker/{webWorker.js => webWorker.ts} (100%) diff --git a/packages/dicom-image-loader/.webpack/webpack-base.js b/packages/dicom-image-loader/.webpack/webpack-base.js index fce51d91a..f6aed0257 100644 --- a/packages/dicom-image-loader/.webpack/webpack-base.js +++ b/packages/dicom-image-loader/.webpack/webpack-base.js @@ -12,7 +12,7 @@ module.exports = { mode: 'development', context, entry: { - cornerstoneWADOImageLoader: './imageLoader/index.js', + cornerstoneWADOImageLoader: './imageLoader/index.ts', }, target: 'web', output: { @@ -35,6 +35,7 @@ module.exports = { }, }, resolve: { + extensions: ['.ts', '.js'], fallback: { fs: false, path: false, @@ -45,7 +46,7 @@ module.exports = { rules: [ { enforce: 'pre', - test: /\.js$/, + test: /\.(mjs|js|ts)$/, exclude: /(node_modules)|(codecs)/, loader: 'eslint-loader', options: { @@ -57,7 +58,7 @@ module.exports = { type: 'asset/resource', }, { - test: /\.worker\.js$/, + test: /\.worker\.(mjs|js|ts)$/, use: [ { loader: 'worker-loader', @@ -68,7 +69,7 @@ module.exports = { ], }, { - test: /\.js$/, + test: /\.(mjs|js|ts)$/, exclude: [/(node_modules)/, /(codecs)/], use: { loader: 'babel-loader', diff --git a/packages/dicom-image-loader/.webpack/webpack-bundle.js b/packages/dicom-image-loader/.webpack/webpack-bundle.js index 5f01476b6..531706b23 100644 --- a/packages/dicom-image-loader/.webpack/webpack-bundle.js +++ b/packages/dicom-image-loader/.webpack/webpack-bundle.js @@ -15,8 +15,8 @@ module.exports = { mode: 'production', context, entry: { - cornerstoneWADOImageLoader: './imageLoader/index.js', - cornerstoneWADOImageLoaderNoWebWorkers: './imageLoader/index-noWorkers.js', + cornerstoneWADOImageLoader: './imageLoader/index.ts', + cornerstoneWADOImageLoaderNoWebWorkers: './imageLoader/index-noWorkers.ts', }, target: 'web', output: { @@ -39,6 +39,7 @@ module.exports = { }, }, resolve: { + extensions: ['.ts', '.js'], fallback: { fs: false, path: false, @@ -49,7 +50,7 @@ module.exports = { rules: [ { enforce: 'pre', - test: /\.js$/, + test: /\.(mjs|js|ts)$/, exclude: /(node_modules)|(codecs)/, loader: 'eslint-loader', options: { @@ -61,7 +62,7 @@ module.exports = { type: 'asset/inline', }, { - test: /\.worker\.js$/, + test: /\.worker\.(mjs|js|ts)$/, use: [ { loader: 'worker-loader', @@ -73,7 +74,7 @@ module.exports = { ], }, { - test: /\.js$/, + test: /\.(mjs|js|ts)$/, exclude: [/(node_modules)/, /(codecs)/], use: { loader: 'babel-loader', diff --git a/packages/dicom-image-loader/.webpack/webpack-esm.js b/packages/dicom-image-loader/.webpack/webpack-esm.js index 354fbf052..2a34322e0 100644 --- a/packages/dicom-image-loader/.webpack/webpack-esm.js +++ b/packages/dicom-image-loader/.webpack/webpack-esm.js @@ -12,7 +12,7 @@ const config = { mode: 'development', context, entry: { - cornerstoneWADOImageLoader: './imageLoader/index.js', + cornerstoneWADOImageLoader: './imageLoader/index.ts', }, target: 'web', output: { @@ -35,6 +35,7 @@ const config = { }, }, resolve: { + extensions: ['.ts', '.js'], fallback: { fs: false, path: false, @@ -57,7 +58,7 @@ const config = { type: 'asset/resource', }, { - test: /\.js$/, + test: /\.(mjs|js|ts)$/, exclude: [/(node_modules)/, /(codecs)/], use: { loader: 'babel-loader', diff --git a/packages/dicom-image-loader/src/externalModules.js b/packages/dicom-image-loader/src/externalModules.ts similarity index 59% rename from packages/dicom-image-loader/src/externalModules.js rename to packages/dicom-image-loader/src/externalModules.ts index d494efb97..18d332f45 100644 --- a/packages/dicom-image-loader/src/externalModules.js +++ b/packages/dicom-image-loader/src/externalModules.ts @@ -1,20 +1,22 @@ /* eslint import/extensions:0 */ import registerLoaders from './imageLoader/registerLoaders'; +import * as cornerstoneImport from '@cornerstonejs/core'; +import * as dicomParserImport from 'dicom-parser'; -let cornerstone; +let cornerstone: typeof cornerstoneImport; -let dicomParser; +let dicomParser: typeof dicomParserImport; const external = { - set cornerstone(cs) { + set cornerstone(cs: typeof cornerstoneImport) { cornerstone = cs; registerLoaders(cornerstone); }, - get cornerstone() { + get cornerstone(): typeof cornerstoneImport { if (!cornerstone) { - if (window && window.cornerstone) { - cornerstone = window.cornerstone; + if (window && (window as any).cornerstone) { + cornerstone = (window as any).cornerstone; registerLoaders(cornerstone); } else { @@ -26,13 +28,13 @@ const external = { return cornerstone; }, - set dicomParser(dp) { + set dicomParser(dp: typeof dicomParserImport) { dicomParser = dp; }, - get dicomParser() { + get dicomParser(): typeof dicomParserImport { if (!dicomParser) { - if (window && window.dicomParser) { - dicomParser = window.dicomParser; + if (window && (window as any).dicomParser) { + dicomParser = (window as any).dicomParser; } else { throw new Error( 'cornerstoneWADOImageLoader requires a copy of dicomParser to work properly. Please add cornerstoneWADOImageLoader.external.dicomParser = dicomParser; to your application.' diff --git a/packages/dicom-image-loader/src/imageLoader/colorSpaceConverters/convertPALETTECOLOR.js b/packages/dicom-image-loader/src/imageLoader/colorSpaceConverters/convertPALETTECOLOR.ts similarity index 100% rename from packages/dicom-image-loader/src/imageLoader/colorSpaceConverters/convertPALETTECOLOR.js rename to packages/dicom-image-loader/src/imageLoader/colorSpaceConverters/convertPALETTECOLOR.ts diff --git a/packages/dicom-image-loader/src/imageLoader/colorSpaceConverters/convertRGBColorByPixel.js b/packages/dicom-image-loader/src/imageLoader/colorSpaceConverters/convertRGBColorByPixel.ts similarity index 100% rename from packages/dicom-image-loader/src/imageLoader/colorSpaceConverters/convertRGBColorByPixel.js rename to packages/dicom-image-loader/src/imageLoader/colorSpaceConverters/convertRGBColorByPixel.ts diff --git a/packages/dicom-image-loader/src/imageLoader/colorSpaceConverters/convertRGBColorByPlane.js b/packages/dicom-image-loader/src/imageLoader/colorSpaceConverters/convertRGBColorByPlane.ts similarity index 100% rename from packages/dicom-image-loader/src/imageLoader/colorSpaceConverters/convertRGBColorByPlane.js rename to packages/dicom-image-loader/src/imageLoader/colorSpaceConverters/convertRGBColorByPlane.ts diff --git a/packages/dicom-image-loader/src/imageLoader/colorSpaceConverters/convertYBRFull422ByPixel.js b/packages/dicom-image-loader/src/imageLoader/colorSpaceConverters/convertYBRFull422ByPixel.ts similarity index 100% rename from packages/dicom-image-loader/src/imageLoader/colorSpaceConverters/convertYBRFull422ByPixel.js rename to packages/dicom-image-loader/src/imageLoader/colorSpaceConverters/convertYBRFull422ByPixel.ts diff --git a/packages/dicom-image-loader/src/imageLoader/colorSpaceConverters/convertYBRFullByPixel.js b/packages/dicom-image-loader/src/imageLoader/colorSpaceConverters/convertYBRFullByPixel.ts similarity index 100% rename from packages/dicom-image-loader/src/imageLoader/colorSpaceConverters/convertYBRFullByPixel.js rename to packages/dicom-image-loader/src/imageLoader/colorSpaceConverters/convertYBRFullByPixel.ts diff --git a/packages/dicom-image-loader/src/imageLoader/colorSpaceConverters/convertYBRFullByPlane.js b/packages/dicom-image-loader/src/imageLoader/colorSpaceConverters/convertYBRFullByPlane.ts similarity index 100% rename from packages/dicom-image-loader/src/imageLoader/colorSpaceConverters/convertYBRFullByPlane.js rename to packages/dicom-image-loader/src/imageLoader/colorSpaceConverters/convertYBRFullByPlane.ts diff --git a/packages/dicom-image-loader/src/imageLoader/colorSpaceConverters/index.js b/packages/dicom-image-loader/src/imageLoader/colorSpaceConverters/index.ts similarity index 100% rename from packages/dicom-image-loader/src/imageLoader/colorSpaceConverters/index.js rename to packages/dicom-image-loader/src/imageLoader/colorSpaceConverters/index.ts diff --git a/packages/dicom-image-loader/src/imageLoader/configure.js b/packages/dicom-image-loader/src/imageLoader/configure.ts similarity index 100% rename from packages/dicom-image-loader/src/imageLoader/configure.js rename to packages/dicom-image-loader/src/imageLoader/configure.ts diff --git a/packages/dicom-image-loader/src/imageLoader/convertColorSpace.js b/packages/dicom-image-loader/src/imageLoader/convertColorSpace.ts similarity index 100% rename from packages/dicom-image-loader/src/imageLoader/convertColorSpace.js rename to packages/dicom-image-loader/src/imageLoader/convertColorSpace.ts diff --git a/packages/dicom-image-loader/src/imageLoader/createImage.js b/packages/dicom-image-loader/src/imageLoader/createImage.ts similarity index 100% rename from packages/dicom-image-loader/src/imageLoader/createImage.js rename to packages/dicom-image-loader/src/imageLoader/createImage.ts diff --git a/packages/dicom-image-loader/src/imageLoader/decodeImageFrame-noWorkers.js b/packages/dicom-image-loader/src/imageLoader/decodeImageFrame-noWorkers.ts similarity index 100% rename from packages/dicom-image-loader/src/imageLoader/decodeImageFrame-noWorkers.js rename to packages/dicom-image-loader/src/imageLoader/decodeImageFrame-noWorkers.ts diff --git a/packages/dicom-image-loader/src/imageLoader/decodeImageFrame.js b/packages/dicom-image-loader/src/imageLoader/decodeImageFrame.ts similarity index 100% rename from packages/dicom-image-loader/src/imageLoader/decodeImageFrame.js rename to packages/dicom-image-loader/src/imageLoader/decodeImageFrame.ts diff --git a/packages/dicom-image-loader/src/imageLoader/decodeJPEGBaseline8BitColor.js b/packages/dicom-image-loader/src/imageLoader/decodeJPEGBaseline8BitColor.ts similarity index 100% rename from packages/dicom-image-loader/src/imageLoader/decodeJPEGBaseline8BitColor.js rename to packages/dicom-image-loader/src/imageLoader/decodeJPEGBaseline8BitColor.ts diff --git a/packages/dicom-image-loader/src/imageLoader/getImageFrame.js b/packages/dicom-image-loader/src/imageLoader/getImageFrame.ts similarity index 100% rename from packages/dicom-image-loader/src/imageLoader/getImageFrame.js rename to packages/dicom-image-loader/src/imageLoader/getImageFrame.ts diff --git a/packages/dicom-image-loader/src/imageLoader/getMinMax.js b/packages/dicom-image-loader/src/imageLoader/getMinMax.ts similarity index 100% rename from packages/dicom-image-loader/src/imageLoader/getMinMax.js rename to packages/dicom-image-loader/src/imageLoader/getMinMax.ts diff --git a/packages/dicom-image-loader/src/imageLoader/getScalingParameters.js b/packages/dicom-image-loader/src/imageLoader/getScalingParameters.ts similarity index 100% rename from packages/dicom-image-loader/src/imageLoader/getScalingParameters.js rename to packages/dicom-image-loader/src/imageLoader/getScalingParameters.ts diff --git a/packages/dicom-image-loader/src/imageLoader/imageIdToURI.js b/packages/dicom-image-loader/src/imageLoader/imageIdToURI.ts similarity index 100% rename from packages/dicom-image-loader/src/imageLoader/imageIdToURI.js rename to packages/dicom-image-loader/src/imageLoader/imageIdToURI.ts diff --git a/packages/dicom-image-loader/src/imageLoader/index-noWorkers.js b/packages/dicom-image-loader/src/imageLoader/index-noWorkers.ts similarity index 100% rename from packages/dicom-image-loader/src/imageLoader/index-noWorkers.js rename to packages/dicom-image-loader/src/imageLoader/index-noWorkers.ts diff --git a/packages/dicom-image-loader/src/imageLoader/index.js b/packages/dicom-image-loader/src/imageLoader/index.ts similarity index 100% rename from packages/dicom-image-loader/src/imageLoader/index.js rename to packages/dicom-image-loader/src/imageLoader/index.ts diff --git a/packages/dicom-image-loader/src/imageLoader/internal/index.js b/packages/dicom-image-loader/src/imageLoader/internal/index.ts similarity index 100% rename from packages/dicom-image-loader/src/imageLoader/internal/index.js rename to packages/dicom-image-loader/src/imageLoader/internal/index.ts diff --git a/packages/dicom-image-loader/src/imageLoader/internal/options.js b/packages/dicom-image-loader/src/imageLoader/internal/options.ts similarity index 100% rename from packages/dicom-image-loader/src/imageLoader/internal/options.js rename to packages/dicom-image-loader/src/imageLoader/internal/options.ts diff --git a/packages/dicom-image-loader/src/imageLoader/internal/xhrRequest.js b/packages/dicom-image-loader/src/imageLoader/internal/xhrRequest.ts similarity index 100% rename from packages/dicom-image-loader/src/imageLoader/internal/xhrRequest.js rename to packages/dicom-image-loader/src/imageLoader/internal/xhrRequest.ts diff --git a/packages/dicom-image-loader/src/imageLoader/isColorImage.js b/packages/dicom-image-loader/src/imageLoader/isColorImage.ts similarity index 100% rename from packages/dicom-image-loader/src/imageLoader/isColorImage.js rename to packages/dicom-image-loader/src/imageLoader/isColorImage.ts diff --git a/packages/dicom-image-loader/src/imageLoader/isJPEGBaseline8BitColor.js b/packages/dicom-image-loader/src/imageLoader/isJPEGBaseline8BitColor.ts similarity index 100% rename from packages/dicom-image-loader/src/imageLoader/isJPEGBaseline8BitColor.js rename to packages/dicom-image-loader/src/imageLoader/isJPEGBaseline8BitColor.ts diff --git a/packages/dicom-image-loader/src/imageLoader/registerLoaders.js b/packages/dicom-image-loader/src/imageLoader/registerLoaders.ts similarity index 100% rename from packages/dicom-image-loader/src/imageLoader/registerLoaders.js rename to packages/dicom-image-loader/src/imageLoader/registerLoaders.ts diff --git a/packages/dicom-image-loader/src/imageLoader/wadors/findIndexOfString.js b/packages/dicom-image-loader/src/imageLoader/wadors/findIndexOfString.ts similarity index 100% rename from packages/dicom-image-loader/src/imageLoader/wadors/findIndexOfString.js rename to packages/dicom-image-loader/src/imageLoader/wadors/findIndexOfString.ts diff --git a/packages/dicom-image-loader/src/imageLoader/wadors/getPixelData.js b/packages/dicom-image-loader/src/imageLoader/wadors/getPixelData.ts similarity index 100% rename from packages/dicom-image-loader/src/imageLoader/wadors/getPixelData.js rename to packages/dicom-image-loader/src/imageLoader/wadors/getPixelData.ts diff --git a/packages/dicom-image-loader/src/imageLoader/wadors/index.js b/packages/dicom-image-loader/src/imageLoader/wadors/index.ts similarity index 100% rename from packages/dicom-image-loader/src/imageLoader/wadors/index.js rename to packages/dicom-image-loader/src/imageLoader/wadors/index.ts diff --git a/packages/dicom-image-loader/src/imageLoader/wadors/loadImage.js b/packages/dicom-image-loader/src/imageLoader/wadors/loadImage.ts similarity index 100% rename from packages/dicom-image-loader/src/imageLoader/wadors/loadImage.js rename to packages/dicom-image-loader/src/imageLoader/wadors/loadImage.ts diff --git a/packages/dicom-image-loader/src/imageLoader/wadors/loadImage_test.js b/packages/dicom-image-loader/src/imageLoader/wadors/loadImage_test.ts similarity index 100% rename from packages/dicom-image-loader/src/imageLoader/wadors/loadImage_test.js rename to packages/dicom-image-loader/src/imageLoader/wadors/loadImage_test.ts diff --git a/packages/dicom-image-loader/src/imageLoader/wadors/metaData/getNumberString.js b/packages/dicom-image-loader/src/imageLoader/wadors/metaData/getNumberString.ts similarity index 100% rename from packages/dicom-image-loader/src/imageLoader/wadors/metaData/getNumberString.js rename to packages/dicom-image-loader/src/imageLoader/wadors/metaData/getNumberString.ts diff --git a/packages/dicom-image-loader/src/imageLoader/wadors/metaData/getNumberValue.js b/packages/dicom-image-loader/src/imageLoader/wadors/metaData/getNumberValue.ts similarity index 100% rename from packages/dicom-image-loader/src/imageLoader/wadors/metaData/getNumberValue.js rename to packages/dicom-image-loader/src/imageLoader/wadors/metaData/getNumberValue.ts diff --git a/packages/dicom-image-loader/src/imageLoader/wadors/metaData/getNumberValues.js b/packages/dicom-image-loader/src/imageLoader/wadors/metaData/getNumberValues.ts similarity index 100% rename from packages/dicom-image-loader/src/imageLoader/wadors/metaData/getNumberValues.js rename to packages/dicom-image-loader/src/imageLoader/wadors/metaData/getNumberValues.ts diff --git a/packages/dicom-image-loader/src/imageLoader/wadors/metaData/getOverlayPlaneModule.js b/packages/dicom-image-loader/src/imageLoader/wadors/metaData/getOverlayPlaneModule.ts similarity index 100% rename from packages/dicom-image-loader/src/imageLoader/wadors/metaData/getOverlayPlaneModule.js rename to packages/dicom-image-loader/src/imageLoader/wadors/metaData/getOverlayPlaneModule.ts diff --git a/packages/dicom-image-loader/src/imageLoader/wadors/metaData/getValue.js b/packages/dicom-image-loader/src/imageLoader/wadors/metaData/getValue.ts similarity index 100% rename from packages/dicom-image-loader/src/imageLoader/wadors/metaData/getValue.js rename to packages/dicom-image-loader/src/imageLoader/wadors/metaData/getValue.ts diff --git a/packages/dicom-image-loader/src/imageLoader/wadors/metaData/index.js b/packages/dicom-image-loader/src/imageLoader/wadors/metaData/index.ts similarity index 100% rename from packages/dicom-image-loader/src/imageLoader/wadors/metaData/index.js rename to packages/dicom-image-loader/src/imageLoader/wadors/metaData/index.ts diff --git a/packages/dicom-image-loader/src/imageLoader/wadors/metaData/metaDataProvider.js b/packages/dicom-image-loader/src/imageLoader/wadors/metaData/metaDataProvider.ts similarity index 100% rename from packages/dicom-image-loader/src/imageLoader/wadors/metaData/metaDataProvider.js rename to packages/dicom-image-loader/src/imageLoader/wadors/metaData/metaDataProvider.ts diff --git a/packages/dicom-image-loader/src/imageLoader/wadors/metaDataManager.js b/packages/dicom-image-loader/src/imageLoader/wadors/metaDataManager.ts similarity index 100% rename from packages/dicom-image-loader/src/imageLoader/wadors/metaDataManager.js rename to packages/dicom-image-loader/src/imageLoader/wadors/metaDataManager.ts diff --git a/packages/dicom-image-loader/src/imageLoader/wadors/register.js b/packages/dicom-image-loader/src/imageLoader/wadors/register.ts similarity index 100% rename from packages/dicom-image-loader/src/imageLoader/wadors/register.js rename to packages/dicom-image-loader/src/imageLoader/wadors/register.ts diff --git a/packages/dicom-image-loader/src/imageLoader/wadouri/dataSetCacheManager.js b/packages/dicom-image-loader/src/imageLoader/wadouri/dataSetCacheManager.ts similarity index 100% rename from packages/dicom-image-loader/src/imageLoader/wadouri/dataSetCacheManager.js rename to packages/dicom-image-loader/src/imageLoader/wadouri/dataSetCacheManager.ts diff --git a/packages/dicom-image-loader/src/imageLoader/wadouri/dataSetCacheManager_test.js b/packages/dicom-image-loader/src/imageLoader/wadouri/dataSetCacheManager_test.ts similarity index 100% rename from packages/dicom-image-loader/src/imageLoader/wadouri/dataSetCacheManager_test.js rename to packages/dicom-image-loader/src/imageLoader/wadouri/dataSetCacheManager_test.ts diff --git a/packages/dicom-image-loader/src/imageLoader/wadouri/fileManager.js b/packages/dicom-image-loader/src/imageLoader/wadouri/fileManager.ts similarity index 100% rename from packages/dicom-image-loader/src/imageLoader/wadouri/fileManager.js rename to packages/dicom-image-loader/src/imageLoader/wadouri/fileManager.ts diff --git a/packages/dicom-image-loader/src/imageLoader/wadouri/getEncapsulatedImageFrame.js b/packages/dicom-image-loader/src/imageLoader/wadouri/getEncapsulatedImageFrame.ts similarity index 100% rename from packages/dicom-image-loader/src/imageLoader/wadouri/getEncapsulatedImageFrame.js rename to packages/dicom-image-loader/src/imageLoader/wadouri/getEncapsulatedImageFrame.ts diff --git a/packages/dicom-image-loader/src/imageLoader/wadouri/getPixelData.js b/packages/dicom-image-loader/src/imageLoader/wadouri/getPixelData.ts similarity index 100% rename from packages/dicom-image-loader/src/imageLoader/wadouri/getPixelData.js rename to packages/dicom-image-loader/src/imageLoader/wadouri/getPixelData.ts diff --git a/packages/dicom-image-loader/src/imageLoader/wadouri/getUncompressedImageFrame.js b/packages/dicom-image-loader/src/imageLoader/wadouri/getUncompressedImageFrame.ts similarity index 100% rename from packages/dicom-image-loader/src/imageLoader/wadouri/getUncompressedImageFrame.js rename to packages/dicom-image-loader/src/imageLoader/wadouri/getUncompressedImageFrame.ts diff --git a/packages/dicom-image-loader/src/imageLoader/wadouri/index.js b/packages/dicom-image-loader/src/imageLoader/wadouri/index.ts similarity index 100% rename from packages/dicom-image-loader/src/imageLoader/wadouri/index.js rename to packages/dicom-image-loader/src/imageLoader/wadouri/index.ts diff --git a/packages/dicom-image-loader/src/imageLoader/wadouri/loadFileRequest.js b/packages/dicom-image-loader/src/imageLoader/wadouri/loadFileRequest.ts similarity index 100% rename from packages/dicom-image-loader/src/imageLoader/wadouri/loadFileRequest.js rename to packages/dicom-image-loader/src/imageLoader/wadouri/loadFileRequest.ts diff --git a/packages/dicom-image-loader/src/imageLoader/wadouri/loadImage.js b/packages/dicom-image-loader/src/imageLoader/wadouri/loadImage.ts similarity index 100% rename from packages/dicom-image-loader/src/imageLoader/wadouri/loadImage.js rename to packages/dicom-image-loader/src/imageLoader/wadouri/loadImage.ts diff --git a/packages/dicom-image-loader/src/imageLoader/wadouri/metaData/getImagePixelModule.js b/packages/dicom-image-loader/src/imageLoader/wadouri/metaData/getImagePixelModule.ts similarity index 100% rename from packages/dicom-image-loader/src/imageLoader/wadouri/metaData/getImagePixelModule.js rename to packages/dicom-image-loader/src/imageLoader/wadouri/metaData/getImagePixelModule.ts diff --git a/packages/dicom-image-loader/src/imageLoader/wadouri/metaData/getLUTs.js b/packages/dicom-image-loader/src/imageLoader/wadouri/metaData/getLUTs.ts similarity index 100% rename from packages/dicom-image-loader/src/imageLoader/wadouri/metaData/getLUTs.js rename to packages/dicom-image-loader/src/imageLoader/wadouri/metaData/getLUTs.ts diff --git a/packages/dicom-image-loader/src/imageLoader/wadouri/metaData/getModalityLUTOutputPixelRepresentation.js b/packages/dicom-image-loader/src/imageLoader/wadouri/metaData/getModalityLUTOutputPixelRepresentation.ts similarity index 100% rename from packages/dicom-image-loader/src/imageLoader/wadouri/metaData/getModalityLUTOutputPixelRepresentation.js rename to packages/dicom-image-loader/src/imageLoader/wadouri/metaData/getModalityLUTOutputPixelRepresentation.ts diff --git a/packages/dicom-image-loader/src/imageLoader/wadouri/metaData/getNumberValues.js b/packages/dicom-image-loader/src/imageLoader/wadouri/metaData/getNumberValues.ts similarity index 100% rename from packages/dicom-image-loader/src/imageLoader/wadouri/metaData/getNumberValues.js rename to packages/dicom-image-loader/src/imageLoader/wadouri/metaData/getNumberValues.ts diff --git a/packages/dicom-image-loader/src/imageLoader/wadouri/metaData/getOverlayPlaneModule.js b/packages/dicom-image-loader/src/imageLoader/wadouri/metaData/getOverlayPlaneModule.ts similarity index 100% rename from packages/dicom-image-loader/src/imageLoader/wadouri/metaData/getOverlayPlaneModule.js rename to packages/dicom-image-loader/src/imageLoader/wadouri/metaData/getOverlayPlaneModule.ts diff --git a/packages/dicom-image-loader/src/imageLoader/wadouri/metaData/index.js b/packages/dicom-image-loader/src/imageLoader/wadouri/metaData/index.ts similarity index 100% rename from packages/dicom-image-loader/src/imageLoader/wadouri/metaData/index.js rename to packages/dicom-image-loader/src/imageLoader/wadouri/metaData/index.ts diff --git a/packages/dicom-image-loader/src/imageLoader/wadouri/metaData/metaDataProvider.js b/packages/dicom-image-loader/src/imageLoader/wadouri/metaData/metaDataProvider.ts similarity index 100% rename from packages/dicom-image-loader/src/imageLoader/wadouri/metaData/metaDataProvider.js rename to packages/dicom-image-loader/src/imageLoader/wadouri/metaData/metaDataProvider.ts diff --git a/packages/dicom-image-loader/src/imageLoader/wadouri/parseImageId.js b/packages/dicom-image-loader/src/imageLoader/wadouri/parseImageId.ts similarity index 100% rename from packages/dicom-image-loader/src/imageLoader/wadouri/parseImageId.js rename to packages/dicom-image-loader/src/imageLoader/wadouri/parseImageId.ts diff --git a/packages/dicom-image-loader/src/imageLoader/wadouri/register.js b/packages/dicom-image-loader/src/imageLoader/wadouri/register.ts similarity index 100% rename from packages/dicom-image-loader/src/imageLoader/wadouri/register.js rename to packages/dicom-image-loader/src/imageLoader/wadouri/register.ts diff --git a/packages/dicom-image-loader/src/imageLoader/wadouri/unpackBinaryFrame.js b/packages/dicom-image-loader/src/imageLoader/wadouri/unpackBinaryFrame.ts similarity index 100% rename from packages/dicom-image-loader/src/imageLoader/wadouri/unpackBinaryFrame.js rename to packages/dicom-image-loader/src/imageLoader/wadouri/unpackBinaryFrame.ts diff --git a/packages/dicom-image-loader/src/imageLoader/webWorkerManager.js b/packages/dicom-image-loader/src/imageLoader/webWorkerManager.ts similarity index 100% rename from packages/dicom-image-loader/src/imageLoader/webWorkerManager.js rename to packages/dicom-image-loader/src/imageLoader/webWorkerManager.ts diff --git a/packages/dicom-image-loader/src/imageLoader/webWorkerManager_test.js b/packages/dicom-image-loader/src/imageLoader/webWorkerManager_test.ts similarity index 100% rename from packages/dicom-image-loader/src/imageLoader/webWorkerManager_test.js rename to packages/dicom-image-loader/src/imageLoader/webWorkerManager_test.ts diff --git a/packages/dicom-image-loader/src/shared/calculateMinMax.js b/packages/dicom-image-loader/src/shared/calculateMinMax.ts similarity index 100% rename from packages/dicom-image-loader/src/shared/calculateMinMax.js rename to packages/dicom-image-loader/src/shared/calculateMinMax.ts diff --git a/packages/dicom-image-loader/src/shared/calculateMinMax_test.js b/packages/dicom-image-loader/src/shared/calculateMinMax_test.ts similarity index 100% rename from packages/dicom-image-loader/src/shared/calculateMinMax_test.js rename to packages/dicom-image-loader/src/shared/calculateMinMax_test.ts diff --git a/packages/dicom-image-loader/src/shared/decodeImageFrame.js b/packages/dicom-image-loader/src/shared/decodeImageFrame.ts similarity index 100% rename from packages/dicom-image-loader/src/shared/decodeImageFrame.js rename to packages/dicom-image-loader/src/shared/decodeImageFrame.ts diff --git a/packages/dicom-image-loader/src/shared/decoders/decodeBigEndian.js b/packages/dicom-image-loader/src/shared/decoders/decodeBigEndian.ts similarity index 100% rename from packages/dicom-image-loader/src/shared/decoders/decodeBigEndian.js rename to packages/dicom-image-loader/src/shared/decoders/decodeBigEndian.ts diff --git a/packages/dicom-image-loader/src/shared/decoders/decodeJPEG2000.js b/packages/dicom-image-loader/src/shared/decoders/decodeJPEG2000.ts similarity index 100% rename from packages/dicom-image-loader/src/shared/decoders/decodeJPEG2000.js rename to packages/dicom-image-loader/src/shared/decoders/decodeJPEG2000.ts diff --git a/packages/dicom-image-loader/src/shared/decoders/decodeJPEGBaseline12Bit-js.js b/packages/dicom-image-loader/src/shared/decoders/decodeJPEGBaseline12Bit-js.ts similarity index 100% rename from packages/dicom-image-loader/src/shared/decoders/decodeJPEGBaseline12Bit-js.js rename to packages/dicom-image-loader/src/shared/decoders/decodeJPEGBaseline12Bit-js.ts diff --git a/packages/dicom-image-loader/src/shared/decoders/decodeJPEGBaseline12Bit-wasm-not-yet-working.js b/packages/dicom-image-loader/src/shared/decoders/decodeJPEGBaseline12Bit-wasm-not-yet-working.ts similarity index 100% rename from packages/dicom-image-loader/src/shared/decoders/decodeJPEGBaseline12Bit-wasm-not-yet-working.js rename to packages/dicom-image-loader/src/shared/decoders/decodeJPEGBaseline12Bit-wasm-not-yet-working.ts diff --git a/packages/dicom-image-loader/src/shared/decoders/decodeJPEGBaseline8Bit.js b/packages/dicom-image-loader/src/shared/decoders/decodeJPEGBaseline8Bit.ts similarity index 100% rename from packages/dicom-image-loader/src/shared/decoders/decodeJPEGBaseline8Bit.js rename to packages/dicom-image-loader/src/shared/decoders/decodeJPEGBaseline8Bit.ts diff --git a/packages/dicom-image-loader/src/shared/decoders/decodeJPEGLS.js b/packages/dicom-image-loader/src/shared/decoders/decodeJPEGLS.ts similarity index 100% rename from packages/dicom-image-loader/src/shared/decoders/decodeJPEGLS.js rename to packages/dicom-image-loader/src/shared/decoders/decodeJPEGLS.ts diff --git a/packages/dicom-image-loader/src/shared/decoders/decodeJPEGLossless.js b/packages/dicom-image-loader/src/shared/decoders/decodeJPEGLossless.ts similarity index 100% rename from packages/dicom-image-loader/src/shared/decoders/decodeJPEGLossless.js rename to packages/dicom-image-loader/src/shared/decoders/decodeJPEGLossless.ts diff --git a/packages/dicom-image-loader/src/shared/decoders/decodeLittleEndian.js b/packages/dicom-image-loader/src/shared/decoders/decodeLittleEndian.ts similarity index 100% rename from packages/dicom-image-loader/src/shared/decoders/decodeLittleEndian.js rename to packages/dicom-image-loader/src/shared/decoders/decodeLittleEndian.ts diff --git a/packages/dicom-image-loader/src/shared/decoders/decodeRLE.js b/packages/dicom-image-loader/src/shared/decoders/decodeRLE.ts similarity index 100% rename from packages/dicom-image-loader/src/shared/decoders/decodeRLE.js rename to packages/dicom-image-loader/src/shared/decoders/decodeRLE.ts diff --git a/packages/dicom-image-loader/src/shared/getMinMax.js b/packages/dicom-image-loader/src/shared/getMinMax.ts similarity index 100% rename from packages/dicom-image-loader/src/shared/getMinMax.js rename to packages/dicom-image-loader/src/shared/getMinMax.ts diff --git a/packages/dicom-image-loader/src/shared/getMinMax_test.js b/packages/dicom-image-loader/src/shared/getMinMax_test.ts similarity index 100% rename from packages/dicom-image-loader/src/shared/getMinMax_test.js rename to packages/dicom-image-loader/src/shared/getMinMax_test.ts diff --git a/packages/dicom-image-loader/src/shared/scaling/scaleArray.js b/packages/dicom-image-loader/src/shared/scaling/scaleArray.ts similarity index 100% rename from packages/dicom-image-loader/src/shared/scaling/scaleArray.js rename to packages/dicom-image-loader/src/shared/scaling/scaleArray.ts diff --git a/packages/dicom-image-loader/src/version.js b/packages/dicom-image-loader/src/version.ts similarity index 100% rename from packages/dicom-image-loader/src/version.js rename to packages/dicom-image-loader/src/version.ts diff --git a/packages/dicom-image-loader/src/webWorker/decodeTask.js b/packages/dicom-image-loader/src/webWorker/decodeTask.ts similarity index 100% rename from packages/dicom-image-loader/src/webWorker/decodeTask.js rename to packages/dicom-image-loader/src/webWorker/decodeTask.ts diff --git a/packages/dicom-image-loader/src/webWorker/index.worker.js b/packages/dicom-image-loader/src/webWorker/index.worker.ts similarity index 100% rename from packages/dicom-image-loader/src/webWorker/index.worker.js rename to packages/dicom-image-loader/src/webWorker/index.worker.ts diff --git a/packages/dicom-image-loader/src/webWorker/webWorker.js b/packages/dicom-image-loader/src/webWorker/webWorker.ts similarity index 100% rename from packages/dicom-image-loader/src/webWorker/webWorker.js rename to packages/dicom-image-loader/src/webWorker/webWorker.ts From edb3eb5bcd49d980124d9b76f06d388fcbbb07c2 Mon Sep 17 00:00:00 2001 From: James Manners Date: Wed, 7 Dec 2022 15:14:05 +1100 Subject: [PATCH 24/25] [wip] initial typescript conversion --- .../convertPALETTECOLOR.ts | 11 +- .../convertRGBColorByPixel.ts | 8 +- .../convertRGBColorByPlane.ts | 8 +- .../convertYBRFull422ByPixel.ts | 8 +- .../convertYBRFullByPixel.ts | 8 +- .../convertYBRFullByPlane.ts | 8 +- .../src/imageLoader/configure.ts | 3 +- .../src/imageLoader/createImage.ts | 64 ++++++--- .../imageLoader/decodeImageFrame-noWorkers.ts | 23 +-- .../src/imageLoader/decodeImageFrame.ts | 26 ++-- .../decodeJPEGBaseline8BitColor.ts | 23 ++- .../src/imageLoader/getImageFrame.ts | 10 +- .../src/imageLoader/getMinMax.ts | 5 +- .../src/imageLoader/getScalingParameters.ts | 10 +- .../src/imageLoader/imageIdToURI.ts | 2 +- .../src/imageLoader/internal/options.ts | 38 ++++- .../src/imageLoader/internal/xhrRequest.ts | 43 ++++-- .../src/imageLoader/isColorImage.ts | 2 +- .../imageLoader/isJPEGBaseline8BitColor.ts | 10 +- .../src/imageLoader/registerLoaders.ts | 3 +- .../src/imageLoader/wado-loader.ts | 18 +++ .../imageLoader/wadors/findIndexOfString.ts | 10 +- .../src/imageLoader/wadors/getPixelData.ts | 18 ++- .../src/imageLoader/wadors/loadImage.ts | 135 ++++++++++-------- .../wadors/metaData/getNumberString.ts | 7 +- .../wadors/metaData/getNumberValue.ts | 6 +- .../wadors/metaData/getNumberValues.ts | 11 +- .../wadors/metaData/getOverlayPlaneModule.ts | 3 +- .../imageLoader/wadors/metaData/getValue.ts | 8 +- .../wadors/metaData/metaDataProvider.ts | 16 +-- .../src/imageLoader/wadors/metaDataManager.ts | 11 +- .../src/imageLoader/wadors/register.ts | 3 +- .../imageLoader/wadors/wado-rs-metadata.ts | 5 + .../wadouri/dataSetCacheManager.ts | 127 +++++++++------- .../src/imageLoader/wadouri/fileManager.ts | 10 +- .../wadouri/getEncapsulatedImageFrame.ts | 8 +- .../src/imageLoader/wadouri/getPixelData.ts | 3 +- .../wadouri/getUncompressedImageFrame.ts | 6 +- .../imageLoader/wadouri/loadFileRequest.ts | 6 +- .../src/imageLoader/wadouri/loadImage.ts | 109 ++++++++------ .../wadouri/metaData/getImagePixelModule.ts | 23 ++- .../imageLoader/wadouri/metaData/getLUTs.ts | 21 ++- ...getModalityLUTOutputPixelRepresentation.ts | 6 +- .../wadouri/metaData/getNumberValues.ts | 8 +- .../wadouri/metaData/getOverlayPlaneModule.ts | 4 +- .../wadouri/metaData/metaDataProvider.ts | 60 ++++++-- .../src/imageLoader/wadouri/parseImageId.ts | 8 +- .../src/imageLoader/wadouri/register.ts | 3 +- .../imageLoader/wadouri/unpackBinaryFrame.ts | 10 +- .../src/imageLoader/webWorkerManager.ts | 92 +++++++++--- .../src/shared/decodeImageFrame.ts | 20 ++- .../src/shared/decoders/decodeBigEndian.ts | 8 +- .../src/shared/decoders/decodeJPEG2000.ts | 13 +- .../decoders/decodeJPEGBaseline12Bit-js.ts | 15 +- .../shared/decoders/decodeJPEGBaseline8Bit.ts | 11 +- .../src/shared/decoders/decodeJPEGLS.ts | 16 ++- .../src/shared/decoders/decodeJPEGLossless.ts | 15 +- .../src/shared/decoders/decodeLittleEndian.ts | 8 +- .../src/shared/decoders/decodeRLE.ts | 17 ++- .../src/shared/getMinMax.ts | 4 +- .../src/shared/image-frame.ts | 30 ++++ .../src/shared/scaling/scaleArray.ts | 5 +- .../src/shared/types/load-image-options.ts | 17 +++ .../src/shared/types/load-request-function.ts | 6 + .../src/shared/types/metadata-modules.ts | 81 +++++++++++ .../src/webWorker/decodeTask.ts | 26 ++-- .../src/webWorker/webWorker.ts | 45 +++++- .../src/webWorker/webworker-messages.ts | 46 ++++++ 68 files changed, 1096 insertions(+), 355 deletions(-) create mode 100644 packages/dicom-image-loader/src/imageLoader/wado-loader.ts create mode 100644 packages/dicom-image-loader/src/imageLoader/wadors/wado-rs-metadata.ts create mode 100644 packages/dicom-image-loader/src/shared/image-frame.ts create mode 100644 packages/dicom-image-loader/src/shared/types/load-image-options.ts create mode 100644 packages/dicom-image-loader/src/shared/types/load-request-function.ts create mode 100644 packages/dicom-image-loader/src/shared/types/metadata-modules.ts create mode 100644 packages/dicom-image-loader/src/webWorker/webworker-messages.ts diff --git a/packages/dicom-image-loader/src/imageLoader/colorSpaceConverters/convertPALETTECOLOR.ts b/packages/dicom-image-loader/src/imageLoader/colorSpaceConverters/convertPALETTECOLOR.ts index 5a9b1f56f..fd7d4f455 100644 --- a/packages/dicom-image-loader/src/imageLoader/colorSpaceConverters/convertPALETTECOLOR.ts +++ b/packages/dicom-image-loader/src/imageLoader/colorSpaceConverters/convertPALETTECOLOR.ts @@ -1,6 +1,9 @@ /* eslint no-bitwise: 0 */ -function convertLUTto8Bit(lut, shift) { +import { CornerstoneWadoImageFrame } from 'dicom-image-loader/src/shared/image-frame'; +import { ByteArray } from 'dicom-parser'; + +function convertLUTto8Bit(lut: number[], shift: number) { const numEntries = lut.length; const cleanedLUT = new Uint8ClampedArray(numEntries); @@ -18,7 +21,11 @@ function convertLUTto8Bit(lut, shift) { * @param {Uint8ClampedArray} colorBuffer * @returns {void} */ -export default function (imageFrame, colorBuffer, useRGBA) { +export default function ( + imageFrame: CornerstoneWadoImageFrame, + colorBuffer: ByteArray, + useRGBA: boolean +): void { const numPixels = imageFrame.columns * imageFrame.rows; const pixelData = imageFrame.pixelData; const rData = imageFrame.redPaletteColorLookupTableData; diff --git a/packages/dicom-image-loader/src/imageLoader/colorSpaceConverters/convertRGBColorByPixel.ts b/packages/dicom-image-loader/src/imageLoader/colorSpaceConverters/convertRGBColorByPixel.ts index 2222c1e3f..0b149aef1 100644 --- a/packages/dicom-image-loader/src/imageLoader/colorSpaceConverters/convertRGBColorByPixel.ts +++ b/packages/dicom-image-loader/src/imageLoader/colorSpaceConverters/convertRGBColorByPixel.ts @@ -1,4 +1,10 @@ -export default function (imageFrame, colorBuffer, useRGBA) { +import { ByteArray } from 'dicom-parser'; + +export default function ( + imageFrame: ByteArray, + colorBuffer: ByteArray, + useRGBA: boolean +): void { if (imageFrame === undefined) { throw new Error('decodeRGB: rgbBuffer must not be undefined'); } diff --git a/packages/dicom-image-loader/src/imageLoader/colorSpaceConverters/convertRGBColorByPlane.ts b/packages/dicom-image-loader/src/imageLoader/colorSpaceConverters/convertRGBColorByPlane.ts index 124d9ee03..2e5f22fce 100644 --- a/packages/dicom-image-loader/src/imageLoader/colorSpaceConverters/convertRGBColorByPlane.ts +++ b/packages/dicom-image-loader/src/imageLoader/colorSpaceConverters/convertRGBColorByPlane.ts @@ -1,4 +1,10 @@ -export default function (imageFrame, colorBuffer, useRGBA) { +import { ByteArray } from 'dicom-parser'; + +export default function ( + imageFrame: ByteArray, + colorBuffer: ByteArray, + useRGBA: boolean +): void { if (imageFrame === undefined) { throw new Error('decodeRGB: rgbBuffer must not be undefined'); } diff --git a/packages/dicom-image-loader/src/imageLoader/colorSpaceConverters/convertYBRFull422ByPixel.ts b/packages/dicom-image-loader/src/imageLoader/colorSpaceConverters/convertYBRFull422ByPixel.ts index 13cc124a5..1083abad0 100644 --- a/packages/dicom-image-loader/src/imageLoader/colorSpaceConverters/convertYBRFull422ByPixel.ts +++ b/packages/dicom-image-loader/src/imageLoader/colorSpaceConverters/convertYBRFull422ByPixel.ts @@ -1,4 +1,10 @@ -export default function (imageFrame, colorBuffer, useRGBA) { +import { ByteArray } from 'dicom-parser'; + +export default function ( + imageFrame: ByteArray, + colorBuffer: ByteArray, + useRGBA: boolean +): void { if (imageFrame === undefined) { throw new Error('decodeRGB: ybrBuffer must not be undefined'); } diff --git a/packages/dicom-image-loader/src/imageLoader/colorSpaceConverters/convertYBRFullByPixel.ts b/packages/dicom-image-loader/src/imageLoader/colorSpaceConverters/convertYBRFullByPixel.ts index dfd46fde9..2ca53110c 100644 --- a/packages/dicom-image-loader/src/imageLoader/colorSpaceConverters/convertYBRFullByPixel.ts +++ b/packages/dicom-image-loader/src/imageLoader/colorSpaceConverters/convertYBRFullByPixel.ts @@ -1,4 +1,10 @@ -export default function (imageFrame, colorBuffer, useRGBA) { +import { ByteArray } from 'dicom-parser'; + +export default function ( + imageFrame: ByteArray, + colorBuffer: ByteArray, + useRGBA: boolean +): void { if (imageFrame === undefined) { throw new Error('decodeRGB: ybrBuffer must not be undefined'); } diff --git a/packages/dicom-image-loader/src/imageLoader/colorSpaceConverters/convertYBRFullByPlane.ts b/packages/dicom-image-loader/src/imageLoader/colorSpaceConverters/convertYBRFullByPlane.ts index ccac454b9..389b6c73c 100644 --- a/packages/dicom-image-loader/src/imageLoader/colorSpaceConverters/convertYBRFullByPlane.ts +++ b/packages/dicom-image-loader/src/imageLoader/colorSpaceConverters/convertYBRFullByPlane.ts @@ -1,4 +1,10 @@ -export default function (imageFrame, colorBuffer, useRGBA) { +import { ByteArray } from 'dicom-parser'; + +export default function ( + imageFrame: ByteArray, + colorBuffer: ByteArray, + useRGBA: boolean +): void { if (imageFrame === undefined) { throw new Error('decodeRGB: ybrBuffer must not be undefined'); } diff --git a/packages/dicom-image-loader/src/imageLoader/configure.ts b/packages/dicom-image-loader/src/imageLoader/configure.ts index 0c6f20b69..3fa07a5c1 100644 --- a/packages/dicom-image-loader/src/imageLoader/configure.ts +++ b/packages/dicom-image-loader/src/imageLoader/configure.ts @@ -1,6 +1,7 @@ import { setOptions } from './internal/index'; +import { CornerstoneWadoLoaderOptions } from './internal/options'; -function configure(options) { +function configure(options: CornerstoneWadoLoaderOptions): void { setOptions(options); } diff --git a/packages/dicom-image-loader/src/imageLoader/createImage.ts b/packages/dicom-image-loader/src/imageLoader/createImage.ts index 9523008bc..4361c0c6e 100644 --- a/packages/dicom-image-loader/src/imageLoader/createImage.ts +++ b/packages/dicom-image-loader/src/imageLoader/createImage.ts @@ -1,16 +1,24 @@ +import { ByteArray } from 'dicom-parser'; import external from '../externalModules'; -import getImageFrame from './getImageFrame'; +import getMinMax from '../shared/getMinMax'; +import { CornerstoneWadoImageFrame } from '../shared/image-frame'; +import { + CornerstoneMetadataImagePlaneModule, + CornerstoneMetadataSopCommonModule, +} from '../shared/types/metadata-modules'; +import convertColorSpace from './convertColorSpace'; +import { CornerstoneLoadImageOptions } from '../shared/types/load-image-options'; import decodeImageFrame from './decodeImageFrame'; +import getImageFrame from './getImageFrame'; +import getScalingParameters from './getScalingParameters'; +import { getOptions } from './internal/options'; import isColorImageFn from './isColorImage'; -import convertColorSpace from './convertColorSpace'; -import getMinMax from '../shared/getMinMax'; import isJPEGBaseline8BitColor from './isJPEGBaseline8BitColor'; -import { getOptions } from './internal/options'; -import getScalingParameters from './getScalingParameters'; +import { CornerstoneWadoLoaderIImage } from './wado-loader'; let lastImageIdDrawn = ''; -function isModalityLUTForDisplay(sopClassUid) { +function isModalityLUTForDisplay(sopClassUid: string): boolean { // special case for XA and XRF // https://groups.google.com/forum/#!searchin/comp.protocols.dicom/Modality$20LUT$20XA/comp.protocols.dicom/UBxhOZ2anJ0/D0R_QP8V2wIJ return ( @@ -54,7 +62,7 @@ function convertToIntPixelData(floatPixelData) { * can transfer array buffers but not typed arrays * @param imageFrame */ -function setPixelDataType(imageFrame) { +function setPixelDataType(imageFrame: CornerstoneWadoImageFrame) { if (imageFrame.bitsAllocated === 32) { imageFrame.pixelData = new Float32Array(imageFrame.pixelData); } else if (imageFrame.bitsAllocated === 16) { @@ -76,7 +84,15 @@ function setPixelDataType(imageFrame) { * @param imageFrame - decoded image in RGBA * @param targetBuffer - target buffer to write to */ -function removeAFromRGBA(imageFrame, targetBuffer) { +function removeAFromRGBA( + imageFrame: + | Float32Array // populated later after decoding + | Int16Array + | Uint16Array + | Uint8Array + | Uint8ClampedArray, + targetBuffer: Uint8ClampedArray +) { const numPixels = imageFrame.length / 4; let rgbIndex = 0; @@ -93,7 +109,12 @@ function removeAFromRGBA(imageFrame, targetBuffer) { return targetBuffer; } -function createImage(imageId, pixelData, transferSyntax, options = {}) { +function createImage( + imageId: string, + pixelData: ByteArray, + transferSyntax: string, + options: CornerstoneLoadImageOptions = {} +): Promise { // whether to use RGBA for color images, default true as cs-legacy uses RGBA // but we don't need RGBA in cs3d, and it's faster, and memory-efficient // in cs3d @@ -144,7 +165,7 @@ function createImage(imageId, pixelData, transferSyntax, options = {}) { const { decodeConfig } = getOptions(); const { convertFloatPixelDataToInt } = decodeConfig; - return new Promise((resolve, reject) => { + return new Promise((resolve, reject) => { // eslint-disable-next-line complexity decodePromise.then(function (imageFrame) { // If we have a target buffer that was written to in the @@ -153,7 +174,7 @@ function createImage(imageId, pixelData, transferSyntax, options = {}) { let alreadyTyped = false; if (options.targetBuffer) { - let offset, length; + let offset: number, length: number; // If we have a target buffer, write to that instead. This helps reduce memory duplication. ({ offset, length } = options.targetBuffer); @@ -210,13 +231,13 @@ function createImage(imageId, pixelData, transferSyntax, options = {}) { setPixelDataType(imageFrame); } - const imagePlaneModule = + const imagePlaneModule: CornerstoneMetadataImagePlaneModule = cornerstone.metaData.get('imagePlaneModule', imageId) || {}; const voiLutModule = cornerstone.metaData.get('voiLutModule', imageId) || {}; const modalityLutModule = cornerstone.metaData.get('modalityLutModule', imageId) || {}; - const sopCommonModule = + const sopCommonModule: CornerstoneMetadataSopCommonModule = cornerstone.metaData.get('sopCommonModule', imageId) || {}; const isColorImage = isColorImageFn(imageFrame.photometricInterpretation); @@ -267,7 +288,8 @@ function createImage(imageId, pixelData, transferSyntax, options = {}) { convertColorSpace(imageFrame, imageData.data, true); - const colorBuffer = new imageData.data.constructor( + /** @todo check as any */ + const colorBuffer = new (imageData.data as any).constructor( (imageData.data.length / 4) * 3 ); @@ -276,14 +298,15 @@ function createImage(imageId, pixelData, transferSyntax, options = {}) { } } + /** @todo check as any */ // calculate smallest and largest PixelValue of the converted pixelData - const minMax = getMinMax(imageFrame.pixelData); + const minMax = getMinMax(imageFrame.pixelData as any); imageFrame.smallestPixelValue = minMax.min; imageFrame.largestPixelValue = minMax.max; } - const image = { + const image: CornerstoneWadoLoaderIImage = { imageId, color: isColorImage, columnPixelSpacing: imagePlaneModule.columnPixelSpacing, @@ -313,6 +336,9 @@ function createImage(imageId, pixelData, transferSyntax, options = {}) { floatPixelData: undefined, imageFrame, rgba: isColorImage && useRGBA, + getPixelData: undefined, + getCanvas: undefined, + numComps: undefined, }; // If pixel data is intrinsically floating 32 array, we convert it to int for @@ -330,9 +356,11 @@ function createImage(imageId, pixelData, transferSyntax, options = {}) { image.slope = results.slope; image.intercept = results.intercept; image.floatPixelData = floatPixelData; - image.getPixelData = () => results.intPixelData; + /** @todo check as any */ + image.getPixelData = () => results.intPixelData as any; } else { - image.getPixelData = () => imageFrame.pixelData; + /** @todo check as any */ + image.getPixelData = () => imageFrame.pixelData as any; } if (image.color) { diff --git a/packages/dicom-image-loader/src/imageLoader/decodeImageFrame-noWorkers.ts b/packages/dicom-image-loader/src/imageLoader/decodeImageFrame-noWorkers.ts index 0438ab10b..9c7f6369b 100644 --- a/packages/dicom-image-loader/src/imageLoader/decodeImageFrame-noWorkers.ts +++ b/packages/dicom-image-loader/src/imageLoader/decodeImageFrame-noWorkers.ts @@ -1,10 +1,17 @@ -import { getOptions } from './internal/options'; import decodeJPEGBaseline8BitColor from './decodeJPEGBaseline8BitColor'; +import { getOptions } from './internal/options'; -import { default as decodeImageFrameHandler } from '../shared/decodeImageFrame'; +import { ByteArray } from 'dicom-parser'; import calculateMinMax from '../shared/calculateMinMax'; +import { default as decodeImageFrameHandler } from '../shared/decodeImageFrame'; +import { CornerstoneWadoImageFrame } from '../shared/image-frame'; -function processDecodeTask(imageFrame, transferSyntax, pixelData, options) { +function processDecodeTask( + imageFrame: CornerstoneWadoImageFrame, + transferSyntax: string, + pixelData: ByteArray, + options +): Promise { const loaderOptions = getOptions(); const { strict, decodeConfig } = loaderOptions; @@ -33,12 +40,12 @@ function processDecodeTask(imageFrame, transferSyntax, pixelData, options) { } function decodeImageFrame( - imageFrame, - transferSyntax, - pixelData, - canvas, + imageFrame: CornerstoneWadoImageFrame, + transferSyntax: string, + pixelData: ByteArray, + canvas: HTMLCanvasElement, options = {} -) { +): Promise { switch (transferSyntax) { case '1.2.840.10008.1.2': // Implicit VR Little Endian diff --git a/packages/dicom-image-loader/src/imageLoader/decodeImageFrame.ts b/packages/dicom-image-loader/src/imageLoader/decodeImageFrame.ts index c5bcca2dd..2248eecfb 100644 --- a/packages/dicom-image-loader/src/imageLoader/decodeImageFrame.ts +++ b/packages/dicom-image-loader/src/imageLoader/decodeImageFrame.ts @@ -5,16 +5,26 @@ import decodeJPEGBaseline8BitColor from './decodeJPEGBaseline8BitColor'; // We only need one function though, so lets import that so we don't make our bundle // too large. import { inflateRaw } from 'pako/lib/inflate'; +import { ByteArray } from 'dicom-parser'; +import { CornerstoneWadoImageFrame } from '../shared/image-frame'; -window.pako = { inflateRaw }; +/** + * @todo check any + */ +(window as any).pako = { inflateRaw }; -function processDecodeTask(imageFrame, transferSyntax, pixelData, options) { +function processDecodeTask( + imageFrame: CornerstoneWadoImageFrame, + transferSyntax: string, + pixelData: ByteArray, + options +): Promise { const priority = options.priority || undefined; const transferList = options.transferPixelData ? [pixelData.buffer] : undefined; - return webWorkerManager.addTask( + return webWorkerManager.addTask( 'decodeTask', { imageFrame, @@ -28,12 +38,12 @@ function processDecodeTask(imageFrame, transferSyntax, pixelData, options) { } function decodeImageFrame( - imageFrame, - transferSyntax, - pixelData, - canvas, + imageFrame: CornerstoneWadoImageFrame, + transferSyntax: string, + pixelData: ByteArray, + canvas: HTMLCanvasElement, options = {} -) { +): Promise { switch (transferSyntax) { case '1.2.840.10008.1.2': // Implicit VR Little Endian diff --git a/packages/dicom-image-loader/src/imageLoader/decodeJPEGBaseline8BitColor.ts b/packages/dicom-image-loader/src/imageLoader/decodeJPEGBaseline8BitColor.ts index f34cbd345..663e7555e 100644 --- a/packages/dicom-image-loader/src/imageLoader/decodeJPEGBaseline8BitColor.ts +++ b/packages/dicom-image-loader/src/imageLoader/decodeJPEGBaseline8BitColor.ts @@ -1,10 +1,12 @@ +import { ByteArray } from 'dicom-parser'; import getMinMax from '../shared/getMinMax'; +import { CornerstoneWadoImageFrame } from '../shared/image-frame'; /** * Special decoder for 8 bit jpeg that leverages the browser's built in JPEG decoder for increased performance */ -function arrayBufferToString(buffer) { +function arrayBufferToString(buffer: ArrayBuffer) { return binaryToString( String.fromCharCode.apply( null, @@ -13,7 +15,7 @@ function arrayBufferToString(buffer) { ); } -function binaryToString(binary) { +function binaryToString(binary: string) { let error; try { @@ -27,7 +29,11 @@ function binaryToString(binary) { } } -function decodeJPEGBaseline8BitColor(imageFrame, pixelData, canvas) { +function decodeJPEGBaseline8BitColor( + imageFrame: CornerstoneWadoImageFrame, + pixelData: ByteArray, + canvas: HTMLCanvasElement +): Promise { const start = new Date().getTime(); const imgBlob = new Blob([pixelData], { type: 'image/jpeg' }); @@ -50,7 +56,10 @@ function decodeJPEGBaseline8BitColor(imageFrame, pixelData, canvas) { imageFrame.columns = img.width; const context = canvas.getContext('2d'); - context.drawImage(this, 0, 0); + /** + * @todo check this context + */ + context.drawImage(this as any, 0, 0); const imageData = context.getImageData(0, 0, img.width, img.height); const end = new Date().getTime(); @@ -73,10 +82,12 @@ function decodeJPEGBaseline8BitColor(imageFrame, pixelData, canvas) { if (fileReader.readAsBinaryString === undefined) { img.src = `data:image/jpeg;base64,${window.btoa( - arrayBufferToString(fileReader.result) + arrayBufferToString(fileReader.result as ArrayBuffer) )}`; } else { - img.src = `data:image/jpeg;base64,${window.btoa(fileReader.result)}`; // doesn't work on IE11 + img.src = `data:image/jpeg;base64,${window.btoa( + fileReader.result as string + )}`; // doesn't work on IE11 } }; diff --git a/packages/dicom-image-loader/src/imageLoader/getImageFrame.ts b/packages/dicom-image-loader/src/imageLoader/getImageFrame.ts index 5673263c2..1c313812a 100644 --- a/packages/dicom-image-loader/src/imageLoader/getImageFrame.ts +++ b/packages/dicom-image-loader/src/imageLoader/getImageFrame.ts @@ -1,11 +1,11 @@ import external from '../externalModules'; +import { CornerstoneWadoImageFrame } from '../shared/image-frame'; +import { CornerstoneMetadataImagePixelModule } from '../shared/types/metadata-modules'; -function getImageFrame(imageId) { +function getImageFrame(imageId: string): CornerstoneWadoImageFrame { const { cornerstone } = external; - const imagePixelModule = cornerstone.metaData.get( - 'imagePixelModule', - imageId - ); + const imagePixelModule: CornerstoneMetadataImagePixelModule = + cornerstone.metaData.get('imagePixelModule', imageId); return { samplesPerPixel: imagePixelModule.samplesPerPixel, diff --git a/packages/dicom-image-loader/src/imageLoader/getMinMax.ts b/packages/dicom-image-loader/src/imageLoader/getMinMax.ts index c59e817e4..b63f021f7 100644 --- a/packages/dicom-image-loader/src/imageLoader/getMinMax.ts +++ b/packages/dicom-image-loader/src/imageLoader/getMinMax.ts @@ -1,4 +1,7 @@ -export default function getMinMax(storedPixelData) { +export default function getMinMax(storedPixelData: ArrayLike): { + min: number; + max: number; +} { // we always calculate the min max values since they are not always // present in DICOM and we don't want to trust them anyway as cornerstone // depends on us providing reliable values for these diff --git a/packages/dicom-image-loader/src/imageLoader/getScalingParameters.ts b/packages/dicom-image-loader/src/imageLoader/getScalingParameters.ts index 30372bc8e..418e3b61a 100644 --- a/packages/dicom-image-loader/src/imageLoader/getScalingParameters.ts +++ b/packages/dicom-image-loader/src/imageLoader/getScalingParameters.ts @@ -1,3 +1,6 @@ +import { CornerstoneMetadataGeneralSeriesModule } from '../shared/types/metadata-modules'; +import type * as cornerstoneImport from '@cornerstonejs/core'; + /** * It returns the scaling parameters for the image with the given imageId. This can be * used to get passed (as an option) to the imageLoader in order to apply scaling to the image inside @@ -5,10 +8,13 @@ * @param imageId - The imageId of the image * @returns ScalingParameters */ -export default function getScalingParameters(metaData, imageId) { +export default function getScalingParameters( + metaData: typeof cornerstoneImport.metaData, + imageId: string +) { const modalityLutModule = metaData.get('modalityLutModule', imageId) || {}; - const generalSeriesModule = + const generalSeriesModule: CornerstoneMetadataGeneralSeriesModule = metaData.get('generalSeriesModule', imageId) || {}; const { modality } = generalSeriesModule; diff --git a/packages/dicom-image-loader/src/imageLoader/imageIdToURI.ts b/packages/dicom-image-loader/src/imageLoader/imageIdToURI.ts index 7be55b075..4813c2c5f 100644 --- a/packages/dicom-image-loader/src/imageLoader/imageIdToURI.ts +++ b/packages/dicom-image-loader/src/imageLoader/imageIdToURI.ts @@ -5,7 +5,7 @@ * @returns {string} imageId without the data loader scheme * @memberof Cache */ -export default function imageIdToURI(imageId) { +export default function imageIdToURI(imageId: string): string { const colonIndex = imageId.indexOf(':'); return imageId.substring(colonIndex + 1); diff --git a/packages/dicom-image-loader/src/imageLoader/internal/options.ts b/packages/dicom-image-loader/src/imageLoader/internal/options.ts index 90cfe4fed..18bc505ab 100644 --- a/packages/dicom-image-loader/src/imageLoader/internal/options.ts +++ b/packages/dicom-image-loader/src/imageLoader/internal/options.ts @@ -1,9 +1,37 @@ -let options = { +import { + CornerstoneWadoLoaderXhrRequestError, + CornerstoneWadoLoaderXhrRequestParams, +} from './xhrRequest'; + +export interface CornerstoneWadoLoaderOptions { + // callback allowing customization of the xhr (e.g. adding custom auth headers, cors, etc) + beforeSend: ( + xhr: XMLHttpRequest, + imageId: string, + defaultHeaders: Record, + params: CornerstoneWadoLoaderXhrRequestParams + ) => Record | void; + // callback allowing modification of the xhr response before creating image objects + beforeProcessing: (xhr: XMLHttpRequest) => Promise; + // callback allowing modification of newly created image objects + imageCreated: (...args: any[]) => void; + onloadstart?: (event: ProgressEvent, params: any) => void; + onloadend?: (event: ProgressEvent, params: any) => void; + onreadystatechange?: (event: Event, params: any) => void; + onprogress?: (event: ProgressEvent, params: any) => void; + errorInterceptor?: (error: CornerstoneWadoLoaderXhrRequestError) => void; + strict: boolean; + decodeConfig: { + convertFloatPixelDataToInt: boolean; + }; +} + +let options: CornerstoneWadoLoaderOptions = { // callback allowing customization of the xhr (e.g. adding custom auth headers, cors, etc) beforeSend(/* xhr, imageId */) {}, // callback allowing modification of the xhr response before creating image objects - beforeProcessing(xhr) { - return Promise.resolve(xhr.response); + beforeProcessing(xhr: XMLHttpRequest) { + return Promise.resolve(xhr.response as ArrayBuffer); }, // callback allowing modification of newly created image objects imageCreated(/* image */) {}, @@ -13,10 +41,10 @@ let options = { }, }; -export function setOptions(newOptions) { +export function setOptions(newOptions: CornerstoneWadoLoaderOptions): void { options = Object.assign(options, newOptions); } -export function getOptions() { +export function getOptions(): CornerstoneWadoLoaderOptions { return options; } diff --git a/packages/dicom-image-loader/src/imageLoader/internal/xhrRequest.ts b/packages/dicom-image-loader/src/imageLoader/internal/xhrRequest.ts index 928558430..dc5fdf7da 100644 --- a/packages/dicom-image-loader/src/imageLoader/internal/xhrRequest.ts +++ b/packages/dicom-image-loader/src/imageLoader/internal/xhrRequest.ts @@ -1,13 +1,38 @@ import external from '../../externalModules'; import { getOptions } from './options'; -function xhrRequest(url, imageId, defaultHeaders = {}, params = {}) { +export interface CornerstoneWadoLoaderXhrRequestError extends Error { + request: XMLHttpRequest; + response: any; + status: number; +} + +/** + * @description mutable object + */ +export interface CornerstoneWadoLoaderXhrRequestParams { + url?: string; + deferred?: { + resolve: (value: ArrayBuffer | PromiseLike) => void; + reject: (reason?: any) => void; + }; + imageId?: string; +} + +function xhrRequest( + url: string, + imageId: string, + defaultHeaders: Record = {}, + params: CornerstoneWadoLoaderXhrRequestParams = {} +): Promise { const { cornerstone } = external; const options = getOptions(); - const errorInterceptor = (xhr) => { + const errorInterceptor = (xhr: XMLHttpRequest) => { if (typeof options.errorInterceptor === 'function') { - const error = new Error('request failed'); + const error = new Error( + 'request failed' + ) as CornerstoneWadoLoaderXhrRequestError; error.request = xhr; error.response = xhr.response; @@ -17,7 +42,7 @@ function xhrRequest(url, imageId, defaultHeaders = {}, params = {}) { }; // Make the request for the DICOM P10 SOP Instance - return new Promise((resolve, reject) => { + return new Promise((resolve, reject) => { const xhr = new XMLHttpRequest(); xhr.open('get', url, true); @@ -63,7 +88,7 @@ function xhrRequest(url, imageId, defaultHeaders = {}, params = {}) { }; cornerstone.triggerEvent( - cornerstone.events, + (cornerstone as any).events, 'cornerstoneimageloadstart', eventData ); @@ -83,7 +108,7 @@ function xhrRequest(url, imageId, defaultHeaders = {}, params = {}) { // Event cornerstone.triggerEvent( - cornerstone.events, + (cornerstone as any).events, 'cornerstoneimageloadend', eventData ); @@ -124,9 +149,9 @@ function xhrRequest(url, imageId, defaultHeaders = {}, params = {}) { // console.log('progress:',oProgress) const loaded = oProgress.loaded; // evt.loaded the bytes browser receive - let total; + let total: number; - let percentComplete; + let percentComplete: number; if (oProgress.lengthComputable) { total = oProgress.total; // evt.total the total bytes seted by the header @@ -148,7 +173,7 @@ function xhrRequest(url, imageId, defaultHeaders = {}, params = {}) { }; cornerstone.triggerEvent( - cornerstone.events, + (cornerstone as any).events, cornerstone.EVENTS.IMAGE_LOAD_PROGRESS, eventData ); diff --git a/packages/dicom-image-loader/src/imageLoader/isColorImage.ts b/packages/dicom-image-loader/src/imageLoader/isColorImage.ts index bc75fffaa..e6edfa634 100644 --- a/packages/dicom-image-loader/src/imageLoader/isColorImage.ts +++ b/packages/dicom-image-loader/src/imageLoader/isColorImage.ts @@ -1,4 +1,4 @@ -export default function (photoMetricInterpretation) { +export default function (photoMetricInterpretation: string): boolean { return ( photoMetricInterpretation === 'RGB' || photoMetricInterpretation === 'PALETTE COLOR' || diff --git a/packages/dicom-image-loader/src/imageLoader/isJPEGBaseline8BitColor.ts b/packages/dicom-image-loader/src/imageLoader/isJPEGBaseline8BitColor.ts index 1b2bf5f16..f9a19994a 100644 --- a/packages/dicom-image-loader/src/imageLoader/isJPEGBaseline8BitColor.ts +++ b/packages/dicom-image-loader/src/imageLoader/isJPEGBaseline8BitColor.ts @@ -1,5 +1,11 @@ -function isJPEGBaseline8BitColor(imageFrame, transferSyntax) { - transferSyntax = transferSyntax || imageFrame.transferSyntax; +import { CornerstoneWadoImageFrame } from '../shared/image-frame'; + +function isJPEGBaseline8BitColor( + imageFrame: CornerstoneWadoImageFrame, + transferSyntax: string +): boolean { + /** @todo check as any */ + transferSyntax = transferSyntax || (imageFrame as any).transferSyntax; if ( imageFrame.bitsAllocated === 8 && diff --git a/packages/dicom-image-loader/src/imageLoader/registerLoaders.ts b/packages/dicom-image-loader/src/imageLoader/registerLoaders.ts index 4ecd98d94..67722ef89 100644 --- a/packages/dicom-image-loader/src/imageLoader/registerLoaders.ts +++ b/packages/dicom-image-loader/src/imageLoader/registerLoaders.ts @@ -1,5 +1,6 @@ import wadors from './wadors/index'; import wadouri from './wadouri/index'; +import * as cornerstoneImport from '@cornerstonejs/core'; /** * Register the WADO-URI and WADO-RS image loaders and metaData providers @@ -7,7 +8,7 @@ import wadouri from './wadouri/index'; * * @param cornerstone The Cornerstone Core library to register the image loaders with */ -function registerLoaders(cornerstone) { +function registerLoaders(cornerstone: typeof cornerstoneImport): void { wadors.register(cornerstone); wadouri.register(cornerstone); } diff --git a/packages/dicom-image-loader/src/imageLoader/wado-loader.ts b/packages/dicom-image-loader/src/imageLoader/wado-loader.ts new file mode 100644 index 000000000..b9a53234b --- /dev/null +++ b/packages/dicom-image-loader/src/imageLoader/wado-loader.ts @@ -0,0 +1,18 @@ +import type { Types } from '@cornerstonejs/core'; +import { ByteArray, DataSet } from 'dicom-parser'; +import { CornerstoneWadoImageFrame } from '../shared/image-frame'; + +export interface CornerstoneWadoLoaderIImage extends Types.IImage { + decodeTimeInMS: number; + floatPixelData?: ByteArray | Float32Array; + loadTimeInMS?: number; + totalTimeInMS?: number; + data?: DataSet; + imageFrame?: CornerstoneWadoImageFrame; +} + +export interface CornerstoneWadoLoaderIImageLoadObject + extends Types.IImageLoadObject { + promise: Promise; + decache?: () => void; +} diff --git a/packages/dicom-image-loader/src/imageLoader/wadors/findIndexOfString.ts b/packages/dicom-image-loader/src/imageLoader/wadors/findIndexOfString.ts index f703ecb66..f5bbd3583 100644 --- a/packages/dicom-image-loader/src/imageLoader/wadors/findIndexOfString.ts +++ b/packages/dicom-image-loader/src/imageLoader/wadors/findIndexOfString.ts @@ -1,4 +1,4 @@ -function checkToken(token, data, dataOffset) { +function checkToken(token, data, dataOffset): boolean { if (dataOffset + token.length > data.length) { return false; } @@ -14,7 +14,7 @@ function checkToken(token, data, dataOffset) { return true; } -function stringToUint8Array(str) { +function stringToUint8Array(str: string): Uint8Array { const uint = new Uint8Array(str.length); for (let i = 0, j = str.length; i < j; i++) { @@ -24,7 +24,11 @@ function stringToUint8Array(str) { return uint; } -function findIndexOfString(data, str, offset) { +function findIndexOfString( + data: Uint8Array, + str: string, + offset?: number +): number { offset = offset || 0; const token = stringToUint8Array(str); diff --git a/packages/dicom-image-loader/src/imageLoader/wadors/getPixelData.ts b/packages/dicom-image-loader/src/imageLoader/wadors/getPixelData.ts index 6135a923d..3fa4e64a0 100644 --- a/packages/dicom-image-loader/src/imageLoader/wadors/getPixelData.ts +++ b/packages/dicom-image-loader/src/imageLoader/wadors/getPixelData.ts @@ -1,7 +1,7 @@ import { xhrRequest } from '../internal/index'; import findIndexOfString from './findIndexOfString'; -function findBoundary(header) { +function findBoundary(header: string[]): string { for (let i = 0; i < header.length; i++) { if (header[i].substr(0, 2) === '--') { return header[i]; @@ -9,7 +9,7 @@ function findBoundary(header) { } } -function findContentType(header) { +function findContentType(header: string[]): string { for (let i = 0; i < header.length; i++) { if (header[i].substr(0, 13) === 'Content-Type:') { return header[i].substr(13).trim(); @@ -29,12 +29,22 @@ function uint8ArrayToString(data, offset, length) { return str; } -function getPixelData(uri, imageId, mediaType = 'application/octet-stream') { +export interface GetPixelDataResponse { + contentType: string; + imageFrame: { + pixelData: Uint8Array; + }; +} +function getPixelData( + uri: string, + imageId: string, + mediaType = 'application/octet-stream' +): Promise { const headers = { Accept: mediaType, }; - return new Promise((resolve, reject) => { + return new Promise((resolve, reject) => { const loadPromise = xhrRequest(uri, imageId, headers); loadPromise.then(function (imageFrameAsArrayBuffer /* , xhr*/) { diff --git a/packages/dicom-image-loader/src/imageLoader/wadors/loadImage.ts b/packages/dicom-image-loader/src/imageLoader/wadors/loadImage.ts index 4adb88502..c18168b52 100644 --- a/packages/dicom-image-loader/src/imageLoader/wadors/loadImage.ts +++ b/packages/dicom-image-loader/src/imageLoader/wadors/loadImage.ts @@ -1,13 +1,18 @@ +import { CornerstoneLoadImageOptions } from 'dicom-image-loader/src/shared/types/load-image-options'; import external from '../../externalModules'; -import getPixelData from './getPixelData'; import createImage from '../createImage'; +import { + CornerstoneWadoLoaderIImage, + CornerstoneWadoLoaderIImageLoadObject, +} from '../wado-loader'; +import getPixelData from './getPixelData'; /** * Helper method to extract the transfer-syntax from the response of the server. * @param {string} contentType The value of the content-type header as returned by the WADO-RS server. * @return The transfer-syntax as announced by the server, or Implicit Little Endian by default. */ -export function getTransferSyntaxForContentType(contentType) { +export function getTransferSyntaxForContentType(contentType: string): string { const defaultTransferSyntax = '1.2.840.10008.1.2'; // Default is Implicit Little Endian. if (!contentType) { @@ -16,7 +21,7 @@ export function getTransferSyntaxForContentType(contentType) { // Browse through the content type parameters const parameters = contentType.split(';'); - const params = {}; + const params: Record = {}; parameters.forEach((parameter) => { // Look for a transfer-syntax=XXXX pair @@ -64,64 +69,82 @@ function getImageRetrievalPool() { return external.cornerstone.imageRetrievalPoolManager; } -function loadImage(imageId, options = {}) { +export interface CornerstoneWadoRsLoaderOptions + extends CornerstoneLoadImageOptions { + requestType?: string; + additionalDetails?: { + imageId: string; + }; + priority?: number; + addToBeginning?: boolean; +} + +function loadImage( + imageId: string, + options: CornerstoneWadoRsLoaderOptions = {} +): CornerstoneWadoLoaderIImageLoadObject { const imageRetrievalPool = getImageRetrievalPool(); const start = new Date().getTime(); - const promise = new Promise((resolve, reject) => { - // TODO: load bulk data items that we might need - - // Uncomment this on to test jpegls codec in OHIF - // const mediaType = 'multipart/related; type="image/x-jls"'; - // const mediaType = 'multipart/related; type="application/octet-stream"; transfer-syntax="image/x-jls"'; - const mediaType = - 'multipart/related; type=application/octet-stream; transfer-syntax=*'; - // const mediaType = - // 'multipart/related; type="image/jpeg"; transfer-syntax=1.2.840.10008.1.2.4.50'; - - function sendXHR(imageURI, imageId, mediaType) { - // get the pixel data from the server - return getPixelData(imageURI, imageId, mediaType) - .then((result) => { - const transferSyntax = getTransferSyntaxForContentType( - result.contentType - ); - const pixelData = result.imageFrame.pixelData; - const imagePromise = createImage( - imageId, - pixelData, - transferSyntax, - options - ); - - imagePromise.then((image) => { - // add the loadTimeInMS property - const end = new Date().getTime(); - - image.loadTimeInMS = end - start; - resolve(image); - }, reject); - }, reject) - .catch((error) => { - reject(error); - }); + const promise = new Promise( + (resolve, reject) => { + // TODO: load bulk data items that we might need + + // Uncomment this on to test jpegls codec in OHIF + // const mediaType = 'multipart/related; type="image/x-jls"'; + // const mediaType = 'multipart/related; type="application/octet-stream"; transfer-syntax="image/x-jls"'; + const mediaType = + 'multipart/related; type=application/octet-stream; transfer-syntax=*'; + // const mediaType = + // 'multipart/related; type="image/jpeg"; transfer-syntax=1.2.840.10008.1.2.4.50'; + + function sendXHR(imageURI: string, imageId: string, mediaType: string) { + // get the pixel data from the server + return getPixelData(imageURI, imageId, mediaType) + .then((result) => { + const transferSyntax = getTransferSyntaxForContentType( + result.contentType + ); + const pixelData = result.imageFrame.pixelData; + const imagePromise = createImage( + imageId, + pixelData, + transferSyntax, + options + ); + + imagePromise.then((image) => { + // add the loadTimeInMS property + const end = new Date().getTime(); + + image.loadTimeInMS = end - start; + resolve(image); + }, reject); + }, reject) + .catch((error) => { + reject(error); + }); + } + + const requestType = options.requestType || 'interaction'; + const additionalDetails = options.additionalDetails || { imageId }; + const priority = options.priority === undefined ? 5 : options.priority; + const addToBeginning = options.addToBeginning || false; + const uri = imageId.substring(7); + + /** + * @todo check arguments + */ + imageRetrievalPool.addRequest( + sendXHR.bind(this, uri, imageId, mediaType), + requestType, + additionalDetails, + priority, + addToBeginning + ); } - - const requestType = options.requestType || 'interaction'; - const additionalDetails = options.additionalDetails || { imageId }; - const priority = options.priority === undefined ? 5 : options.priority; - const addToBeginning = options.addToBeginning || false; - const uri = imageId.substring(7); - - imageRetrievalPool.addRequest( - sendXHR.bind(this, uri, imageId, mediaType), - requestType, - additionalDetails, - priority, - addToBeginning - ); - }); + ); return { promise, diff --git a/packages/dicom-image-loader/src/imageLoader/wadors/metaData/getNumberString.ts b/packages/dicom-image-loader/src/imageLoader/wadors/metaData/getNumberString.ts index aae181c53..8c3ed5d01 100644 --- a/packages/dicom-image-loader/src/imageLoader/wadors/metaData/getNumberString.ts +++ b/packages/dicom-image-loader/src/imageLoader/wadors/metaData/getNumberString.ts @@ -1,3 +1,4 @@ +import { WadoRsMetaDataElement } from '../wado-rs-metadata'; import getValue from './getValue'; /** @@ -8,7 +9,11 @@ import getValue from './getValue'; * @param [defaultValue] - The default value to return if the element does not exist * @returns {*} */ -function getNumberString(element, index, defaultValue) { +function getNumberString( + element: WadoRsMetaDataElement, + index: number, + defaultValue +) { const value = getValue(element, index, defaultValue); if (value === undefined) { diff --git a/packages/dicom-image-loader/src/imageLoader/wadors/metaData/getNumberValue.ts b/packages/dicom-image-loader/src/imageLoader/wadors/metaData/getNumberValue.ts index ad1c39374..a76e038d7 100644 --- a/packages/dicom-image-loader/src/imageLoader/wadors/metaData/getNumberValue.ts +++ b/packages/dicom-image-loader/src/imageLoader/wadors/metaData/getNumberValue.ts @@ -1,6 +1,10 @@ +import { WadoRsMetaDataElement } from '../wado-rs-metadata'; import getValue from './getValue'; -function getNumberValue(element, index) { +function getNumberValue( + element: WadoRsMetaDataElement, + index?: number +): number { const value = getValue(element, index); if (value === undefined) { diff --git a/packages/dicom-image-loader/src/imageLoader/wadors/metaData/getNumberValues.ts b/packages/dicom-image-loader/src/imageLoader/wadors/metaData/getNumberValues.ts index 8a1199598..983831953 100644 --- a/packages/dicom-image-loader/src/imageLoader/wadors/metaData/getNumberValues.ts +++ b/packages/dicom-image-loader/src/imageLoader/wadors/metaData/getNumberValues.ts @@ -1,3 +1,5 @@ +import { WadoRsMetaDataElement } from '../wado-rs-metadata'; + /** * Returns the values as an array of javascript numbers * @@ -5,7 +7,10 @@ * @param [minimumLength] - the minimum number of values * @returns {*} */ -function getNumberValues(element, minimumLength) { +function getNumberValues( + element: WadoRsMetaDataElement, + minimumLength?: number +): number[] { if (!element) { return; } @@ -18,10 +23,10 @@ function getNumberValues(element, minimumLength) { return; } - const values = []; + const values: number[] = []; for (let i = 0; i < element.Value.length; i++) { - values.push(parseFloat(element.Value[i])); + values.push(parseFloat(element.Value[i] as string)); } return values; diff --git a/packages/dicom-image-loader/src/imageLoader/wadors/metaData/getOverlayPlaneModule.ts b/packages/dicom-image-loader/src/imageLoader/wadors/metaData/getOverlayPlaneModule.ts index 516a572ce..e460f5c07 100644 --- a/packages/dicom-image-loader/src/imageLoader/wadors/metaData/getOverlayPlaneModule.ts +++ b/packages/dicom-image-loader/src/imageLoader/wadors/metaData/getOverlayPlaneModule.ts @@ -1,7 +1,8 @@ import getValue from './getValue'; import getNumberValue from './getNumberValue'; +import { WadoRsMetaData } from '../wado-rs-metadata'; -export default function getOverlayPlaneModule(metaData) { +export default function getOverlayPlaneModule(metaData: WadoRsMetaData) { const overlays = []; for (let overlayGroup = 0x00; overlayGroup <= 0x1e; overlayGroup += 0x02) { diff --git a/packages/dicom-image-loader/src/imageLoader/wadors/metaData/getValue.ts b/packages/dicom-image-loader/src/imageLoader/wadors/metaData/getValue.ts index 334b46e7b..816a6b8ec 100644 --- a/packages/dicom-image-loader/src/imageLoader/wadors/metaData/getValue.ts +++ b/packages/dicom-image-loader/src/imageLoader/wadors/metaData/getValue.ts @@ -1,3 +1,5 @@ +import { WadoRsMetaDataElement } from '../wado-rs-metadata'; + /** * Returns the raw value * @@ -6,7 +8,11 @@ * @param [defaultValue] - The default value to return if the element does not exist * @returns {*} */ -function getValue(element, index, defaultValue) { +function getValue( + element: WadoRsMetaDataElement, + index?: number, + defaultValue?: number | string +) { index = index || 0; if (!element) { return defaultValue; diff --git a/packages/dicom-image-loader/src/imageLoader/wadors/metaData/metaDataProvider.ts b/packages/dicom-image-loader/src/imageLoader/wadors/metaData/metaDataProvider.ts index 13db5d719..1a4d0d2b4 100644 --- a/packages/dicom-image-loader/src/imageLoader/wadors/metaData/metaDataProvider.ts +++ b/packages/dicom-image-loader/src/imageLoader/wadors/metaData/metaDataProvider.ts @@ -5,7 +5,7 @@ import getNumberValue from './getNumberValue'; import getOverlayPlaneModule from './getOverlayPlaneModule'; import metaDataManager from '../metaDataManager'; -function metaDataProvider(type, imageId) { +function metaDataProvider(type: string, imageId: string) { const { dicomParser } = external; const metaData = metaDataManager.get(imageId); @@ -52,14 +52,14 @@ function metaDataProvider(type, imageId) { if (imageOrientationPatient) { rowCosines = [ - parseFloat(imageOrientationPatient[0]), - parseFloat(imageOrientationPatient[1]), - parseFloat(imageOrientationPatient[2]), + parseFloat(imageOrientationPatient[0] as any), + parseFloat(imageOrientationPatient[1] as any), + parseFloat(imageOrientationPatient[2] as any), ]; columnCosines = [ - parseFloat(imageOrientationPatient[3]), - parseFloat(imageOrientationPatient[4]), - parseFloat(imageOrientationPatient[5]), + parseFloat(imageOrientationPatient[3] as any), + parseFloat(imageOrientationPatient[4] as any), + parseFloat(imageOrientationPatient[5] as any), ]; } @@ -142,7 +142,7 @@ function metaDataProvider(type, imageId) { return { radiopharmaceuticalInfo: { radiopharmaceuticalStartTime: dicomParser.parseTM( - getValue(radiopharmaceuticalInfo['00181072'], 0, '') + getValue(radiopharmaceuticalInfo['00181072'], 0, '') as string ), radionuclideTotalDose: getNumberValue( radiopharmaceuticalInfo['00181074'] diff --git a/packages/dicom-image-loader/src/imageLoader/wadors/metaDataManager.ts b/packages/dicom-image-loader/src/imageLoader/wadors/metaDataManager.ts index 1139e2699..c84369cb6 100644 --- a/packages/dicom-image-loader/src/imageLoader/wadors/metaDataManager.ts +++ b/packages/dicom-image-loader/src/imageLoader/wadors/metaDataManager.ts @@ -1,26 +1,27 @@ import imageIdToURI from '../imageIdToURI'; +import { WadoRsMetaData } from './wado-rs-metadata'; -let metadataByImageURI = []; +let metadataByImageURI: WadoRsMetaData[] = []; -function add(imageId, metadata) { +function add(imageId: string, metadata: WadoRsMetaData): void { const imageURI = imageIdToURI(imageId); metadataByImageURI[imageURI] = metadata; } -function get(imageId) { +function get(imageId: string): WadoRsMetaData { const imageURI = imageIdToURI(imageId); return metadataByImageURI[imageURI]; } -function remove(imageId) { +function remove(imageId: string): void { const imageURI = imageIdToURI(imageId); metadataByImageURI[imageURI] = undefined; } -function purge() { +function purge(): void { metadataByImageURI = []; } diff --git a/packages/dicom-image-loader/src/imageLoader/wadors/register.ts b/packages/dicom-image-loader/src/imageLoader/wadors/register.ts index 3451be2d5..913dd9079 100644 --- a/packages/dicom-image-loader/src/imageLoader/wadors/register.ts +++ b/packages/dicom-image-loader/src/imageLoader/wadors/register.ts @@ -1,7 +1,8 @@ +import * as cornerstoneImport from '@cornerstonejs/core'; import loadImage from './loadImage'; import { metaDataProvider } from './metaData/index'; -export default function (cornerstone) { +export default function (cornerstone: typeof cornerstoneImport): void { // register wadors scheme and metadata provider cornerstone.registerImageLoader('wadors', loadImage); cornerstone.metaData.addProvider(metaDataProvider); diff --git a/packages/dicom-image-loader/src/imageLoader/wadors/wado-rs-metadata.ts b/packages/dicom-image-loader/src/imageLoader/wadors/wado-rs-metadata.ts new file mode 100644 index 000000000..f455c724c --- /dev/null +++ b/packages/dicom-image-loader/src/imageLoader/wadors/wado-rs-metadata.ts @@ -0,0 +1,5 @@ +export interface WadoRsMetaDataElement { + Value: string[] | number[]; +} + +export type WadoRsMetaData = Record; diff --git a/packages/dicom-image-loader/src/imageLoader/wadouri/dataSetCacheManager.ts b/packages/dicom-image-loader/src/imageLoader/wadouri/dataSetCacheManager.ts index 529383411..5728b8aa6 100644 --- a/packages/dicom-image-loader/src/imageLoader/wadouri/dataSetCacheManager.ts +++ b/packages/dicom-image-loader/src/imageLoader/wadouri/dataSetCacheManager.ts @@ -1,5 +1,7 @@ +import { DataSet } from 'dicom-parser'; import external from '../../externalModules'; import { xhrRequest } from '../internal/index'; +import { CornerstoneWadoLoaderLoadRequestFunction } from '../../shared/types/load-request-function'; /** * This object supports loading of DICOM P10 dataset from a uri and caching it so it can be accessed @@ -9,16 +11,21 @@ import { xhrRequest } from '../internal/index'; */ let cacheSizeInBytes = 0; -let loadedDataSets = {}; +let loadedDataSets: Record = + {}; -let promises = {}; +let promises: Record = {}; + +export interface CornerstoneWadoLoaderCachedPromise extends Promise { + cacheCount?: number; +} // returns true if the wadouri for the specified index has been loaded -function isLoaded(uri) { +function isLoaded(uri: string): boolean { return loadedDataSets[uri] !== undefined; } -function get(uri) { +function get(uri: string): DataSet { if (!loadedDataSets[uri]) { return; } @@ -27,7 +34,11 @@ function get(uri) { } // loads the dicom dataset from the wadouri sp -function load(uri, loadRequest = xhrRequest, imageId) { +function load( + uri: string, + loadRequest: CornerstoneWadoLoaderLoadRequestFunction = xhrRequest, + imageId: string +): CornerstoneWadoLoaderCachedPromise { const { cornerstone, dicomParser } = external; // if already loaded return it right away @@ -51,44 +62,50 @@ function load(uri, loadRequest = xhrRequest, imageId) { const loadDICOMPromise = loadRequest(uri, imageId); // handle success and failure of the XHR request load - const promise = new Promise((resolve, reject) => { - loadDICOMPromise - .then(function (dicomPart10AsArrayBuffer /* , xhr*/) { - const byteArray = new Uint8Array(dicomPart10AsArrayBuffer); - - // Reject the promise if parsing the dicom file fails - let dataSet; - - try { - dataSet = dicomParser.parseDicom(byteArray); - } catch (error) { - return reject(error); - } - - loadedDataSets[uri] = { - dataSet, - cacheCount: promise.cacheCount, - }; - cacheSizeInBytes += dataSet.byteArray.length; - resolve(dataSet); - - cornerstone.triggerEvent(cornerstone.events, 'datasetscachechanged', { - uri, - action: 'loaded', - cacheInfo: getInfo(), - }); - }, reject) - .then( - () => { - // Remove the promise if success - delete promises[uri]; - }, - () => { - // Remove the promise if failure - delete promises[uri]; - } - ); - }); + const promise: CornerstoneWadoLoaderCachedPromise = new Promise( + (resolve, reject) => { + loadDICOMPromise + .then(function (dicomPart10AsArrayBuffer /* , xhr*/) { + const byteArray = new Uint8Array(dicomPart10AsArrayBuffer); + + // Reject the promise if parsing the dicom file fails + let dataSet; + + try { + dataSet = dicomParser.parseDicom(byteArray); + } catch (error) { + return reject(error); + } + + loadedDataSets[uri] = { + dataSet, + cacheCount: promise.cacheCount, + }; + cacheSizeInBytes += dataSet.byteArray.length; + resolve(dataSet); + + cornerstone.triggerEvent( + (cornerstone as any).events, + 'datasetscachechanged', + { + uri, + action: 'loaded', + cacheInfo: getInfo(), + } + ); + }, reject) + .then( + () => { + // Remove the promise if success + delete promises[uri]; + }, + () => { + // Remove the promise if failure + delete promises[uri]; + } + ); + } + ); promise.cacheCount = 1; @@ -98,7 +115,7 @@ function load(uri, loadRequest = xhrRequest, imageId) { } // remove the cached/loaded dicom dataset for the specified wadouri to free up memory -function unload(uri) { +function unload(uri: string): void { const { cornerstone } = external; // console.log('unload for ' + uri); @@ -109,16 +126,24 @@ function unload(uri) { cacheSizeInBytes -= loadedDataSets[uri].dataSet.byteArray.length; delete loadedDataSets[uri]; - cornerstone.triggerEvent(cornerstone.events, 'datasetscachechanged', { - uri, - action: 'unloaded', - cacheInfo: getInfo(), - }); + cornerstone.triggerEvent( + (cornerstone as any).events, + 'datasetscachechanged', + { + uri, + action: 'unloaded', + cacheInfo: getInfo(), + } + ); } } } -export function getInfo() { +export interface CornerstoneWadoLoaderCacheManagerInfoResponse { + cacheSizeInBytes: number; + numberOfDataSetsCached: number; +} +export function getInfo(): CornerstoneWadoLoaderCacheManagerInfoResponse { return { cacheSizeInBytes, numberOfDataSetsCached: Object.keys(loadedDataSets).length, @@ -126,7 +151,7 @@ export function getInfo() { } // removes all cached datasets from memory -function purge() { +function purge(): void { loadedDataSets = {}; promises = {}; cacheSizeInBytes = 0; diff --git a/packages/dicom-image-loader/src/imageLoader/wadouri/fileManager.ts b/packages/dicom-image-loader/src/imageLoader/wadouri/fileManager.ts index 6b9853bc2..10fbc9aa7 100644 --- a/packages/dicom-image-loader/src/imageLoader/wadouri/fileManager.ts +++ b/packages/dicom-image-loader/src/imageLoader/wadouri/fileManager.ts @@ -1,20 +1,20 @@ -let files = []; +let files: Blob[] = []; -function add(file) { +function add(file: Blob): string { const fileIndex = files.push(file); return `dicomfile:${fileIndex - 1}`; } -function get(index) { +function get(index: number): Blob { return files[index]; } -function remove(index) { +function remove(index: number): void { files[index] = undefined; } -function purge() { +function purge(): void { files = []; } diff --git a/packages/dicom-image-loader/src/imageLoader/wadouri/getEncapsulatedImageFrame.ts b/packages/dicom-image-loader/src/imageLoader/wadouri/getEncapsulatedImageFrame.ts index 87869458e..0c14ea70f 100644 --- a/packages/dicom-image-loader/src/imageLoader/wadouri/getEncapsulatedImageFrame.ts +++ b/packages/dicom-image-loader/src/imageLoader/wadouri/getEncapsulatedImageFrame.ts @@ -1,17 +1,21 @@ +import { ByteArray, DataSet } from 'dicom-parser'; import external from '../../externalModules'; /** * Function to deal with extracting an image frame from an encapsulated data set. */ -function framesAreFragmented(dataSet) { +function framesAreFragmented(dataSet: DataSet) { const numberOfFrames = dataSet.intString('x00280008'); const pixelDataElement = dataSet.elements.x7fe00010; return numberOfFrames !== pixelDataElement.fragments.length; } -export default function getEncapsulatedImageFrame(dataSet, frameIndex) { +export default function getEncapsulatedImageFrame( + dataSet: DataSet, + frameIndex: number +): ByteArray { const { dicomParser } = external; if ( diff --git a/packages/dicom-image-loader/src/imageLoader/wadouri/getPixelData.ts b/packages/dicom-image-loader/src/imageLoader/wadouri/getPixelData.ts index 5afac52e1..a2748b73c 100644 --- a/packages/dicom-image-loader/src/imageLoader/wadouri/getPixelData.ts +++ b/packages/dicom-image-loader/src/imageLoader/wadouri/getPixelData.ts @@ -1,7 +1,8 @@ +import { ByteArray, DataSet } from 'dicom-parser'; import getEncapsulatedImageFrame from './getEncapsulatedImageFrame'; import getUncompressedImageFrame from './getUncompressedImageFrame'; -function getPixelData(dataSet, frameIndex = 0) { +function getPixelData(dataSet: DataSet, frameIndex = 0): ByteArray { const pixelDataElement = dataSet.elements.x7fe00010 || dataSet.elements.x7fe00008; diff --git a/packages/dicom-image-loader/src/imageLoader/wadouri/getUncompressedImageFrame.ts b/packages/dicom-image-loader/src/imageLoader/wadouri/getUncompressedImageFrame.ts index 264ef71b8..10d1eeee8 100644 --- a/packages/dicom-image-loader/src/imageLoader/wadouri/getUncompressedImageFrame.ts +++ b/packages/dicom-image-loader/src/imageLoader/wadouri/getUncompressedImageFrame.ts @@ -1,10 +1,14 @@ +import { DataSet } from 'dicom-parser'; import unpackBinaryFrame from './unpackBinaryFrame'; /** * Function to deal with extracting an image frame from an encapsulated data set. */ -function getUncompressedImageFrame(dataSet, frameIndex) { +function getUncompressedImageFrame( + dataSet: DataSet, + frameIndex: number +): Uint8Array { const pixelDataElement = dataSet.elements.x7fe00010 || dataSet.elements.x7fe00008; const bitsAllocated = dataSet.uint16('x00280100'); diff --git a/packages/dicom-image-loader/src/imageLoader/wadouri/loadFileRequest.ts b/packages/dicom-image-loader/src/imageLoader/wadouri/loadFileRequest.ts index 781fa704a..20d5a8c0e 100644 --- a/packages/dicom-image-loader/src/imageLoader/wadouri/loadFileRequest.ts +++ b/packages/dicom-image-loader/src/imageLoader/wadouri/loadFileRequest.ts @@ -1,16 +1,16 @@ import parseImageId from './parseImageId'; import fileManager from './fileManager'; -function loadFileRequest(uri) { +function loadFileRequest(uri: string): Promise { const parsedImageId = parseImageId(uri); const fileIndex = parseInt(parsedImageId.url, 10); const file = fileManager.get(fileIndex); - return new Promise((resolve, reject) => { + return new Promise((resolve, reject) => { const fileReader = new FileReader(); fileReader.onload = (e) => { - const dicomPart10AsArrayBuffer = e.target.result; + const dicomPart10AsArrayBuffer = e.target.result as ArrayBuffer; resolve(dicomPart10AsArrayBuffer); }; diff --git a/packages/dicom-image-loader/src/imageLoader/wadouri/loadImage.ts b/packages/dicom-image-loader/src/imageLoader/wadouri/loadImage.ts index 785170c0f..624ae2a82 100644 --- a/packages/dicom-image-loader/src/imageLoader/wadouri/loadImage.ts +++ b/packages/dicom-image-loader/src/imageLoader/wadouri/loadImage.ts @@ -1,12 +1,22 @@ +import { CornerstoneLoadImageOptions } from 'dicom-image-loader/src/shared/types/load-image-options'; +import { DataSet } from 'dicom-parser'; import createImage from '../createImage'; -import parseImageId from './parseImageId'; +import { xhrRequest } from '../internal/index'; +import { + CornerstoneWadoLoaderIImage, + CornerstoneWadoLoaderIImageLoadObject, +} from '../wado-loader'; import dataSetCacheManager from './dataSetCacheManager'; -import loadFileRequest from './loadFileRequest'; +import { CornerstoneWadoLoaderLoadRequestFunction } from '../../shared/types/load-request-function'; import getPixelData from './getPixelData'; -import { xhrRequest } from '../internal/index'; +import loadFileRequest from './loadFileRequest'; +import parseImageId from './parseImageId'; // add a decache callback function to clear out our dataSetCacheManager -function addDecache(imageLoadObject, imageId) { +function addDecache( + imageLoadObject: CornerstoneWadoLoaderIImageLoadObject, + imageId: string +) { imageLoadObject.decache = function () { // console.log('decache'); const parsedImageId = parseImageId(imageId); @@ -16,16 +26,19 @@ function addDecache(imageLoadObject, imageId) { } function loadImageFromPromise( - dataSetPromise, - imageId, + dataSetPromise: Promise, + imageId: string, frame = 0, - sharedCacheKey, - options, - callbacks -) { + sharedCacheKey: string, + options: CornerstoneLoadImageOptions, + callbacks?: { + imageDoneCallback: (image: CornerstoneWadoLoaderIImage) => void; + } +): CornerstoneWadoLoaderIImageLoadObject { const start = new Date().getTime(); - const imageLoadObject = { + const imageLoadObject: CornerstoneWadoLoaderIImageLoadObject = { cancelFn: undefined, + promise: undefined, }; imageLoadObject.promise = new Promise((resolve, reject) => { @@ -82,43 +95,45 @@ function loadImageFromPromise( function loadImageFromDataSet( dataSet, - imageId, + imageId: string, frame = 0, - sharedCacheKey, + sharedCacheKey: string, options -) { +): CornerstoneWadoLoaderIImageLoadObject { const start = new Date().getTime(); - const promise = new Promise((resolve, reject) => { - const loadEnd = new Date().getTime(); + const promise = new Promise( + (resolve, reject) => { + const loadEnd = new Date().getTime(); - let imagePromise; + let imagePromise: Promise; - try { - const pixelData = getPixelData(dataSet, frame); - const transferSyntax = dataSet.string('x00020010'); + try { + const pixelData = getPixelData(dataSet, frame); + const transferSyntax = dataSet.string('x00020010'); - imagePromise = createImage(imageId, pixelData, transferSyntax, options); - } catch (error) { - // Reject the error, and the dataSet - reject({ - error, - dataSet, - }); + imagePromise = createImage(imageId, pixelData, transferSyntax, options); + } catch (error) { + // Reject the error, and the dataSet + reject({ + error, + dataSet, + }); - return; - } + return; + } - imagePromise.then((image) => { - image.data = dataSet; - image.sharedCacheKey = sharedCacheKey; - const end = new Date().getTime(); + imagePromise.then((image) => { + image.data = dataSet; + image.sharedCacheKey = sharedCacheKey; + const end = new Date().getTime(); - image.loadTimeInMS = loadEnd - start; - image.totalTimeInMS = end - start; - resolve(image); - }, reject); - }); + image.loadTimeInMS = loadEnd - start; + image.totalTimeInMS = end - start; + resolve(image); + }, reject); + } + ); return { promise, @@ -126,7 +141,9 @@ function loadImageFromDataSet( }; } -function getLoaderForScheme(scheme) { +function getLoaderForScheme( + scheme: string +): CornerstoneWadoLoaderLoadRequestFunction { if (scheme === 'dicomweb' || scheme === 'wadouri') { return xhrRequest; } else if (scheme === 'dicomfile') { @@ -134,7 +151,10 @@ function getLoaderForScheme(scheme) { } } -function loadImage(imageId, options = {}) { +function loadImage( + imageId: string, + options: CornerstoneLoadImageOptions = {} +): CornerstoneWadoLoaderIImageLoadObject { const parsedImageId = parseImageId(imageId); options = Object.assign({}, options); @@ -148,7 +168,14 @@ function loadImage(imageId, options = {}) { // if the dataset for this url is already loaded, use it if (dataSetCacheManager.isLoaded(parsedImageId.url)) { - const dataSet = dataSetCacheManager.get(parsedImageId.url, loader, imageId); + /** + * @todo The arguments to the dataSetCacheManager below are incorrect. + */ + const dataSet: DataSet = (dataSetCacheManager as any).get( + parsedImageId.url, + loader, + imageId + ); return loadImageFromDataSet( dataSet, diff --git a/packages/dicom-image-loader/src/imageLoader/wadouri/metaData/getImagePixelModule.ts b/packages/dicom-image-loader/src/imageLoader/wadouri/metaData/getImagePixelModule.ts index 27db277de..e19512edb 100644 --- a/packages/dicom-image-loader/src/imageLoader/wadouri/metaData/getImagePixelModule.ts +++ b/packages/dicom-image-loader/src/imageLoader/wadouri/metaData/getImagePixelModule.ts @@ -1,4 +1,7 @@ -function getLutDescriptor(dataSet, tag) { +import { CornerstoneMetadataImagePixelModule } from 'dicom-image-loader/src/shared/types/metadata-modules'; +import { DataSet } from 'dicom-parser'; + +function getLutDescriptor(dataSet: DataSet, tag: string) { if (!dataSet.elements[tag] || dataSet.elements[tag].length !== 6) { return; } @@ -10,7 +13,7 @@ function getLutDescriptor(dataSet, tag) { ]; } -function getLutData(lutDataSet, tag, lutDescriptor) { +function getLutData(lutDataSet: DataSet, tag: string, lutDescriptor): number[] { const lut = []; const lutData = lutDataSet.elements[tag]; @@ -26,7 +29,10 @@ function getLutData(lutDataSet, tag, lutDescriptor) { return lut; } -function populatePaletteColorLut(dataSet, imagePixelModule) { +function populatePaletteColorLut( + dataSet: DataSet, + imagePixelModule: CornerstoneMetadataImagePixelModule +) { imagePixelModule.redPaletteColorLookupTableDescriptor = getLutDescriptor( dataSet, 'x00281101' @@ -91,7 +97,10 @@ function populatePaletteColorLut(dataSet, imagePixelModule) { ); } -function populateSmallestLargestPixelValues(dataSet, imagePixelModule) { +function populateSmallestLargestPixelValues( + dataSet: DataSet, + imagePixelModule: CornerstoneMetadataImagePixelModule +) { const pixelRepresentation = dataSet.uint16('x00280103'); if (pixelRepresentation === 0) { @@ -103,7 +112,9 @@ function populateSmallestLargestPixelValues(dataSet, imagePixelModule) { } } -function getImagePixelModule(dataSet) { +function getImagePixelModule( + dataSet: DataSet +): CornerstoneMetadataImagePixelModule { const imagePixelModule = { samplesPerPixel: dataSet.uint16('x00280002'), photometricInterpretation: dataSet.string('x00280004'), @@ -115,7 +126,7 @@ function getImagePixelModule(dataSet) { pixelRepresentation: dataSet.uint16('x00280103'), planarConfiguration: dataSet.uint16('x00280006'), pixelAspectRatio: dataSet.string('x00280034'), - }; + } as CornerstoneMetadataImagePixelModule; populateSmallestLargestPixelValues(dataSet, imagePixelModule); diff --git a/packages/dicom-image-loader/src/imageLoader/wadouri/metaData/getLUTs.ts b/packages/dicom-image-loader/src/imageLoader/wadouri/metaData/getLUTs.ts index ebb761481..6dd0281b5 100644 --- a/packages/dicom-image-loader/src/imageLoader/wadouri/metaData/getLUTs.ts +++ b/packages/dicom-image-loader/src/imageLoader/wadouri/metaData/getLUTs.ts @@ -1,4 +1,16 @@ -function getLUT(pixelRepresentation, lutDataSet) { +import { DataSet, Element } from 'dicom-parser'; + +export interface CornerstoneWadoLoaderLut { + id: string; + firstValueMapped: number; + numBitsPerEntry: number; + lut: number[]; +} + +function getLUT( + pixelRepresentation: number, + lutDataSet: DataSet +): CornerstoneWadoLoaderLut { let numLUTEntries = lutDataSet.uint16('x00283002', 0); if (numLUTEntries === 0) { @@ -32,11 +44,14 @@ function getLUT(pixelRepresentation, lutDataSet) { return lut; } -function getLUTs(pixelRepresentation, lutSequence) { +function getLUTs( + pixelRepresentation: number, + lutSequence: Element +): CornerstoneWadoLoaderLut[] { if (!lutSequence || !lutSequence.items || !lutSequence.items.length) { return; } - const luts = []; + const luts: CornerstoneWadoLoaderLut[] = []; for (let i = 0; i < lutSequence.items.length; i++) { const lutDataSet = lutSequence.items[i].dataSet; diff --git a/packages/dicom-image-loader/src/imageLoader/wadouri/metaData/getModalityLUTOutputPixelRepresentation.ts b/packages/dicom-image-loader/src/imageLoader/wadouri/metaData/getModalityLUTOutputPixelRepresentation.ts index 72f7cdedb..d42b78896 100644 --- a/packages/dicom-image-loader/src/imageLoader/wadouri/metaData/getModalityLUTOutputPixelRepresentation.ts +++ b/packages/dicom-image-loader/src/imageLoader/wadouri/metaData/getModalityLUTOutputPixelRepresentation.ts @@ -1,6 +1,8 @@ /* eslint no-bitwise: 0 */ -function getMinStoredPixelValue(dataSet) { +import { DataSet } from 'dicom-parser'; + +function getMinStoredPixelValue(dataSet: DataSet) { const pixelRepresentation = dataSet.uint16('x00280103'); const bitsStored = dataSet.uint16('x00280101'); @@ -12,7 +14,7 @@ function getMinStoredPixelValue(dataSet) { } // 0 = unsigned / US, 1 = signed / SS -function getModalityLUTOutputPixelRepresentation(dataSet) { +function getModalityLUTOutputPixelRepresentation(dataSet: DataSet) { // CT SOP Classes are always signed const sopClassUID = dataSet.string('x00080016'); diff --git a/packages/dicom-image-loader/src/imageLoader/wadouri/metaData/getNumberValues.ts b/packages/dicom-image-loader/src/imageLoader/wadouri/metaData/getNumberValues.ts index a2f9ee918..e429ddfc9 100644 --- a/packages/dicom-image-loader/src/imageLoader/wadouri/metaData/getNumberValues.ts +++ b/packages/dicom-image-loader/src/imageLoader/wadouri/metaData/getNumberValues.ts @@ -1,4 +1,10 @@ -function getNumberValues(dataSet, tag, minimumLength) { +import { DataSet } from 'dicom-parser'; + +function getNumberValues( + dataSet: DataSet, + tag: string, + minimumLength: number +): number[] { const values = []; const valueAsString = dataSet.string(tag); diff --git a/packages/dicom-image-loader/src/imageLoader/wadouri/metaData/getOverlayPlaneModule.ts b/packages/dicom-image-loader/src/imageLoader/wadouri/metaData/getOverlayPlaneModule.ts index ecdce907d..b5a000ffe 100644 --- a/packages/dicom-image-loader/src/imageLoader/wadouri/metaData/getOverlayPlaneModule.ts +++ b/packages/dicom-image-loader/src/imageLoader/wadouri/metaData/getOverlayPlaneModule.ts @@ -1,4 +1,6 @@ -export default function getOverlayPlaneModule(dataSet) { +import { DataSet } from 'dicom-parser'; + +export default function getOverlayPlaneModule(dataSet: DataSet) { const overlays = []; for (let overlayGroup = 0x00; overlayGroup <= 0x1e; overlayGroup += 0x02) { diff --git a/packages/dicom-image-loader/src/imageLoader/wadouri/metaData/metaDataProvider.ts b/packages/dicom-image-loader/src/imageLoader/wadouri/metaData/metaDataProvider.ts index 6d7de0dd1..8dedb279a 100644 --- a/packages/dicom-image-loader/src/imageLoader/wadouri/metaData/metaDataProvider.ts +++ b/packages/dicom-image-loader/src/imageLoader/wadouri/metaData/metaDataProvider.ts @@ -1,13 +1,49 @@ +import { + CornerstoneMetadataGeneralSeriesModule, + CornerstoneMetadataImagePixelModule, + CornerstoneMetadataImagePlaneModule, + CornerstoneMetadataPatientStudyModule, + CornerstoneMetadataSopCommonModule, + CornerstoneMetadataTransferSyntax, + CornerstoneMetaDataTypes, +} from 'dicom-image-loader/src/shared/types/metadata-modules'; import external from '../../../externalModules'; -import getNumberValues from './getNumberValues'; -import parseImageId from '../parseImageId'; import dataSetCacheManager from '../dataSetCacheManager'; +import parseImageId from '../parseImageId'; import getImagePixelModule from './getImagePixelModule'; -import getOverlayPlaneModule from './getOverlayPlaneModule'; import getLUTs from './getLUTs'; import getModalityLUTOutputPixelRepresentation from './getModalityLUTOutputPixelRepresentation'; +import getNumberValues from './getNumberValues'; +import getOverlayPlaneModule from './getOverlayPlaneModule'; -function metaDataProvider(type, imageId) { +function metaDataProvider( + type: 'generalSeriesModule', + imageId: string +): CornerstoneMetadataGeneralSeriesModule; +function metaDataProvider( + type: 'patientStudyModule', + imageId: string +): CornerstoneMetadataPatientStudyModule; +function metaDataProvider( + type: 'imagePlaneModule', + imageId: string +): CornerstoneMetadataImagePlaneModule; +function metaDataProvider( + type: 'imagePixelModule', + imageId: string +): CornerstoneMetadataImagePixelModule; +function metaDataProvider( + type: 'transferSyntax', + imageId: string +): CornerstoneMetadataTransferSyntax; +function metaDataProvider( + type: 'sopCommonModule', + imageId: string +): CornerstoneMetadataSopCommonModule; +function metaDataProvider( + type: CornerstoneMetaDataTypes, + imageId: string +): any { const { dicomParser } = external; const parsedImageId = parseImageId(imageId); @@ -41,9 +77,9 @@ function metaDataProvider(type, imageId) { const imagePositionPatient = getNumberValues(dataSet, 'x00200032', 3); const pixelSpacing = getNumberValues(dataSet, 'x00280030', 2); - let columnPixelSpacing = null; + let columnPixelSpacing: number = null; - let rowPixelSpacing = null; + let rowPixelSpacing: number = null; if (pixelSpacing) { rowPixelSpacing = pixelSpacing[0]; @@ -56,14 +92,14 @@ function metaDataProvider(type, imageId) { if (imageOrientationPatient) { rowCosines = [ - parseFloat(imageOrientationPatient[0]), - parseFloat(imageOrientationPatient[1]), - parseFloat(imageOrientationPatient[2]), + parseFloat(imageOrientationPatient[0] as any), + parseFloat(imageOrientationPatient[1] as any), + parseFloat(imageOrientationPatient[2] as any), ]; columnCosines = [ - parseFloat(imageOrientationPatient[3]), - parseFloat(imageOrientationPatient[4]), - parseFloat(imageOrientationPatient[5]), + parseFloat(imageOrientationPatient[3] as any), + parseFloat(imageOrientationPatient[4] as any), + parseFloat(imageOrientationPatient[5] as any), ]; } diff --git a/packages/dicom-image-loader/src/imageLoader/wadouri/parseImageId.ts b/packages/dicom-image-loader/src/imageLoader/wadouri/parseImageId.ts index 07e7e804e..9fcd71a07 100644 --- a/packages/dicom-image-loader/src/imageLoader/wadouri/parseImageId.ts +++ b/packages/dicom-image-loader/src/imageLoader/wadouri/parseImageId.ts @@ -1,4 +1,10 @@ -function parseImageId(imageId) { +export interface CornerstoneImageUrl { + scheme: string; + url: string; + frame: number; +} + +function parseImageId(imageId: string): CornerstoneImageUrl { // build a url by parsing out the url scheme and frame index from the imageId const firstColonIndex = imageId.indexOf(':'); diff --git a/packages/dicom-image-loader/src/imageLoader/wadouri/register.ts b/packages/dicom-image-loader/src/imageLoader/wadouri/register.ts index ed24e59d1..e5c477a49 100644 --- a/packages/dicom-image-loader/src/imageLoader/wadouri/register.ts +++ b/packages/dicom-image-loader/src/imageLoader/wadouri/register.ts @@ -1,7 +1,8 @@ +import * as cornerstoneImport from '@cornerstonejs/core'; import { loadImage } from './loadImage'; import { metaDataProvider } from './metaData/index'; -export default function (cornerstone) { +export default function (cornerstone: typeof cornerstoneImport): void { // register dicomweb and wadouri image loader prefixes cornerstone.registerImageLoader('dicomweb', loadImage); cornerstone.registerImageLoader('wadouri', loadImage); diff --git a/packages/dicom-image-loader/src/imageLoader/wadouri/unpackBinaryFrame.ts b/packages/dicom-image-loader/src/imageLoader/wadouri/unpackBinaryFrame.ts index 22a5dbfb2..e416ec2db 100644 --- a/packages/dicom-image-loader/src/imageLoader/wadouri/unpackBinaryFrame.ts +++ b/packages/dicom-image-loader/src/imageLoader/wadouri/unpackBinaryFrame.ts @@ -1,13 +1,19 @@ /* eslint no-bitwise: 0 */ -function isBitSet(byte, bitPos) { +import { ByteArray } from 'dicom-parser'; + +function isBitSet(byte: number, bitPos: number) { return byte & (1 << bitPos); } /** * Function to deal with unpacking a binary frame */ -function unpackBinaryFrame(byteArray, frameOffset, pixelsPerFrame) { +function unpackBinaryFrame( + byteArray: ByteArray, + frameOffset: number, + pixelsPerFrame: number +): Uint8Array { // Create a new pixel array given the image size const pixelData = new Uint8Array(pixelsPerFrame); diff --git a/packages/dicom-image-loader/src/imageLoader/webWorkerManager.ts b/packages/dicom-image-loader/src/imageLoader/webWorkerManager.ts index 8c099412b..301cd1420 100644 --- a/packages/dicom-image-loader/src/imageLoader/webWorkerManager.ts +++ b/packages/dicom-image-loader/src/imageLoader/webWorkerManager.ts @@ -2,6 +2,10 @@ // eslint-disable-next-line // import cornerstoneWADOImageLoaderWebWorker from 'worker-loader!../webWorker/index.worker'; import cornerstoneWADOImageLoaderWebWorker from '../webWorker/index.worker'; +import { + CornerstoneWadoWebWorkerDecodeTaskData, + CornerstoneWadoWebWorkerResponse, +} from '../webWorker/webworker-messages'; // This is for the Webpack 5 approch but it's currently broken // so we will continue relying on worker-loader for now @@ -16,16 +20,56 @@ import { getOptions } from './internal/options'; // the taskId to assign to the next task added via addTask() let nextTaskId = 0; +export interface CornerstoneWadoWebWorkerDecodeConfig { + initializeCodecsOnStartup: boolean; + strict: boolean; +} + +export interface CornerstoneWadoWebWorkerTaskOptions { + decodeTask: CornerstoneWadoWebWorkerDecodeConfig; +} + +interface CornerstoneWebWorkerDeferredObject { + resolve: (arg: T | PromiseLike) => void; + reject: (err: any) => void; +} +export type CornerstoneWadoWorkerTaskTypes = + | 'decodeTask' + | 'loadWebWorkerTask' + | 'initialize'; + // array of queued tasks sorted with highest priority task first -const tasks = []; +export interface CornerstoneWorkerTask { + taskId: number; + taskType: CornerstoneWadoWorkerTaskTypes; + status: 'ready' | 'success' | 'failed'; + added: number; + start?: number; + data: CornerstoneWadoWebWorkerDecodeTaskData; + deferred: CornerstoneWebWorkerDeferredObject; + priority: number; + transferList: Transferable[]; +} +const tasks: CornerstoneWorkerTask[] = []; // array of web workers to dispatch decode tasks to -const webWorkers = []; +const webWorkers: { + worker: Worker; + status: 'ready' | 'busy' | 'initializing'; + task?: CornerstoneWorkerTask; +}[] = []; // The options for CornerstoneWADOImageLoader const options = getOptions(); -const defaultConfig = { +export interface CornerstoneWadoWebWorkerOptions { + maxWebWorkers?: number; + startWebWorkersOnDemand?: boolean; + webWorkerTaskPaths?: string[]; + taskConfiguration?: CornerstoneWadoWebWorkerTaskOptions; +} + +const defaultConfig: CornerstoneWadoWebWorkerOptions = { maxWebWorkers: navigator.hardwareConcurrency || 1, startWebWorkersOnDemand: true, webWorkerTaskPaths: [], @@ -37,7 +81,7 @@ const defaultConfig = { }, }; -let config; +let config: CornerstoneWadoWebWorkerOptions; const statistics = { maxWebWorkers: 0, @@ -101,7 +145,9 @@ function startTaskOnWebWorker() { * Function to handle a message from a web worker * @param msg */ -function handleMessageFromWorker(msg) { +function handleMessageFromWorker( + msg: MessageEvent +) { // console.log('handleMessageFromWorker', msg.data); if (msg.data.taskType === 'initialize') { webWorkers[msg.data.workerIndex].status = 'ready'; @@ -137,7 +183,7 @@ function spawnWebWorker() { } // spawn the webworker - const worker = new cornerstoneWADOImageLoaderWebWorker(); + const worker: Worker = new (cornerstoneWADOImageLoaderWebWorker as any)(); // This is for the Webpack 5 approach but it's currently broken /* const worker = new Worker(cornerstoneWADOImageLoaderWebWorkerPath, { @@ -168,7 +214,7 @@ function spawnWebWorker() { * Initialization function for the web worker manager - spawns web workers * @param configObject */ -function initialize(configObject) { +function initialize(configObject?: CornerstoneWadoWebWorkerOptions): void { configObject = configObject || defaultConfig; // prevent being initialized more than once @@ -192,7 +238,7 @@ function initialize(configObject) { /** * Terminate all running web workers. */ -function terminate() { +function terminate(): void { for (let i = 0; i < webWorkers.length; i++) { webWorkers[i].worker.terminate(); } @@ -205,7 +251,7 @@ function terminate() { * @param sourcePath * @param taskConfig */ -function loadWebWorkerTask(sourcePath, taskConfig) { +function loadWebWorkerTask(sourcePath: string, taskConfig): void { // add it to the list of web worker tasks paths so on demand web workers // load this properly config.webWorkerTaskPaths.push(sourcePath); @@ -238,13 +284,21 @@ function loadWebWorkerTask(sourcePath, taskConfig) { * @param transferList - optional array of data to transfer to web worker * @returns {*} */ -function addTask(taskType, data, priority = 0, transferList) { +function addTask( + taskType: CornerstoneWadoWorkerTaskTypes, + data: CornerstoneWadoWebWorkerDecodeTaskData, + priority = 0, + transferList: Transferable[] +): { taskId: number; promise: Promise } { if (!config) { initialize(); } - let deferred = {}; - const promise = new Promise((resolve, reject) => { + let deferred: CornerstoneWebWorkerDeferredObject = { + resolve: undefined, + reject: undefined, + }; + const promise = new Promise((resolve, reject) => { deferred = { resolve, reject, @@ -252,7 +306,7 @@ function addTask(taskType, data, priority = 0, transferList) { }); // find the right spot to insert this decode task (based on priority) - let i; + let i: number; for (i = 0; i < tasks.length; i++) { if (tasks[i].priority < priority) { @@ -289,7 +343,7 @@ function addTask(taskType, data, priority = 0, transferList) { * @param priority - priority of the task (defaults to 0), > 0 is higher, < 0 is lower * @returns boolean - true on success, false if taskId not found */ -function setTaskPriority(taskId, priority = 0) { +function setTaskPriority(taskId: number, priority = 0): boolean { // search for this taskId for (let i = 0; i < tasks.length; i++) { if (tasks[i].taskId === taskId) { @@ -322,14 +376,18 @@ function setTaskPriority(taskId, priority = 0) { * @param reason - optional reason the task was rejected * @returns boolean - true on success, false if taskId not found */ -function cancelTask(taskId, reason) { +function cancelTask(taskId: number, reason: string): boolean { // search for this taskId for (let i = 0; i < tasks.length; i++) { if (tasks[i].taskId === taskId) { // taskId found, remove it + /** + * @todo Check if this is a bug. Splice returns an array but task is + * treated as a single task object + */ const task = tasks.splice(i, 1); - task.deferred.reject(reason); + (task as any).deferred.reject(reason); return true; } @@ -342,7 +400,7 @@ function cancelTask(taskId, reason) { * Function to return the statistics on running web workers * @returns object containing statistics */ -function getStatistics() { +function getStatistics(): typeof config { statistics.maxWebWorkers = config.maxWebWorkers; statistics.numWebWorkers = webWorkers.length; statistics.numTasksQueued = tasks.length; diff --git a/packages/dicom-image-loader/src/shared/decodeImageFrame.ts b/packages/dicom-image-loader/src/shared/decodeImageFrame.ts index 8904777c7..ee90e5445 100644 --- a/packages/dicom-image-loader/src/shared/decodeImageFrame.ts +++ b/packages/dicom-image-loader/src/shared/decodeImageFrame.ts @@ -9,18 +9,20 @@ import decodeJPEGLossless from './decoders/decodeJPEGLossless'; import decodeJPEGLS from './decoders/decodeJPEGLS'; import decodeJPEG2000 from './decoders/decodeJPEG2000'; import scaleArray from './scaling/scaleArray'; +import { ByteArray } from 'dicom-parser'; +import { CornerstoneWadoImageFrame } from './image-frame'; function decodeImageFrame( - imageFrame, - transferSyntax, - pixelData, + imageFrame: CornerstoneWadoImageFrame, + transferSyntax: string, + pixelData: ByteArray, decodeConfig, options, - callbackFn -) { + callbackFn: (...args: any[]) => void +): void { const start = new Date().getTime(); - let decodePromise = null; + let decodePromise: Promise = null; let opts; @@ -141,7 +143,11 @@ function decodeImageFrame( }); } -function postProcessDecodedPixels(imageFrame, options, start) { +function postProcessDecodedPixels( + imageFrame: CornerstoneWadoImageFrame, + options, + start: number +) { const shouldShift = imageFrame.pixelRepresentation !== undefined && imageFrame.pixelRepresentation === 1; diff --git a/packages/dicom-image-loader/src/shared/decoders/decodeBigEndian.ts b/packages/dicom-image-loader/src/shared/decoders/decodeBigEndian.ts index 53cde8cd6..771b85b5d 100644 --- a/packages/dicom-image-loader/src/shared/decoders/decodeBigEndian.ts +++ b/packages/dicom-image-loader/src/shared/decoders/decodeBigEndian.ts @@ -1,9 +1,15 @@ +import { ByteArray } from 'dicom-parser'; +import { CornerstoneWadoImageFrame } from '../image-frame'; + /* eslint no-bitwise: 0 */ function swap16(val) { return ((val & 0xff) << 8) | ((val >> 8) & 0xff); } -async function decodeBigEndian(imageFrame, pixelData) { +async function decodeBigEndian( + imageFrame: CornerstoneWadoImageFrame, + pixelData: ByteArray +): Promise { if (imageFrame.bitsAllocated === 16) { let arrayBuffer = pixelData.buffer; diff --git a/packages/dicom-image-loader/src/shared/decoders/decodeJPEG2000.ts b/packages/dicom-image-loader/src/shared/decoders/decodeJPEG2000.ts index 39dbdb7e6..c74bcb3a1 100644 --- a/packages/dicom-image-loader/src/shared/decoders/decodeJPEG2000.ts +++ b/packages/dicom-image-loader/src/shared/decoders/decodeJPEG2000.ts @@ -7,14 +7,18 @@ import openJpegFactory from '@cornerstonejs/codec-openjpeg/dist/openjpegwasm_dec // This is closer to what Webpack 5 wants but it doesn't seem to work now // const wasm = new URL('./blah.wasm', import.meta.url) import openjpegWasm from '@cornerstonejs/codec-openjpeg/dist/openjpegwasm_decode.wasm'; +import { CornerstoneWadoWebWorkerDecodeConfig } from 'dicom-image-loader/src/imageLoader/webWorkerManager'; +import { CornerstoneWadoImageFrame } from '../image-frame'; const local = { codec: undefined, decoder: undefined, - decodeConfig: {}, + decodeConfig: {} as CornerstoneWadoWebWorkerDecodeConfig, }; -export function initialize(decodeConfig) { +export function initialize( + decodeConfig?: CornerstoneWadoWebWorkerDecodeConfig +): Promise { local.decodeConfig = decodeConfig; if (local.codec) { @@ -41,7 +45,10 @@ export function initialize(decodeConfig) { } // https://github.com/chafey/openjpegjs/blob/master/test/browser/index.html -async function decodeAsync(compressedImageFrame, imageInfo) { +async function decodeAsync( + compressedImageFrame, + imageInfo +): Promise { await initialize(); const decoder = local.decoder; diff --git a/packages/dicom-image-loader/src/shared/decoders/decodeJPEGBaseline12Bit-js.ts b/packages/dicom-image-loader/src/shared/decoders/decodeJPEGBaseline12Bit-js.ts index 2a54b8c67..aea807db8 100644 --- a/packages/dicom-image-loader/src/shared/decoders/decodeJPEGBaseline12Bit-js.ts +++ b/packages/dicom-image-loader/src/shared/decoders/decodeJPEGBaseline12Bit-js.ts @@ -1,9 +1,15 @@ +import { CornerstoneWadoWebWorkerDecodeConfig } from 'dicom-image-loader/src/imageLoader/webWorkerManager'; +import { ByteArray } from 'dicom-parser'; +import { CornerstoneWadoImageFrame } from '../image-frame'; + const local = { JpegImage: undefined, - decodeConfig: {}, + decodeConfig: {} as CornerstoneWadoWebWorkerDecodeConfig, }; -export function initialize(decodeConfig) { +export function initialize( + decodeConfig?: CornerstoneWadoWebWorkerDecodeConfig +): Promise { local.decodeConfig = decodeConfig; if (local.JpegImage) { @@ -18,7 +24,10 @@ export function initialize(decodeConfig) { }); } -async function decodeJPEGBaseline12BitAsync(imageFrame, pixelData) { +async function decodeJPEGBaseline12BitAsync( + imageFrame: CornerstoneWadoImageFrame, + pixelData: ByteArray +): Promise { // check to make sure codec is loaded await initialize(); if (typeof local.JpegImage === 'undefined') { diff --git a/packages/dicom-image-loader/src/shared/decoders/decodeJPEGBaseline8Bit.ts b/packages/dicom-image-loader/src/shared/decoders/decodeJPEGBaseline8Bit.ts index 1faa1f34c..4c0d348a2 100644 --- a/packages/dicom-image-loader/src/shared/decoders/decodeJPEGBaseline8Bit.ts +++ b/packages/dicom-image-loader/src/shared/decoders/decodeJPEGBaseline8Bit.ts @@ -2,13 +2,15 @@ import libjpegTurboFactory from '@cornerstonejs/codec-libjpeg-turbo-8bit/dist/li // Webpack asset/resource copies this to our output folder import libjpegTurboWasm from '@cornerstonejs/codec-libjpeg-turbo-8bit/dist/libjpegturbowasm_decode.wasm'; +import { ByteArray } from 'dicom-parser'; +import { CornerstoneWadoImageFrame } from '../image-frame'; const local = { codec: undefined, decoder: undefined, }; -function initLibjpegTurbo() { +function initLibjpegTurbo(): Promise { if (local.codec) { return Promise.resolve(); } @@ -39,7 +41,10 @@ function initLibjpegTurbo() { * @param {object} imageInfo * @param {boolean} imageInfo.signed - */ -async function decodeAsync(compressedImageFrame, imageInfo) { +async function decodeAsync( + compressedImageFrame, + imageInfo +): Promise { await initLibjpegTurbo(); const decoder = local.decoder; @@ -86,7 +91,7 @@ async function decodeAsync(compressedImageFrame, imageInfo) { }; } -function getPixelData(frameInfo, decodedBuffer) { +function getPixelData(frameInfo, decodedBuffer: ByteArray) { if (frameInfo.isSigned) { return new Int8Array( decodedBuffer.buffer, diff --git a/packages/dicom-image-loader/src/shared/decoders/decodeJPEGLS.ts b/packages/dicom-image-loader/src/shared/decoders/decodeJPEGLS.ts index 73b8656c8..d2b54bf1e 100644 --- a/packages/dicom-image-loader/src/shared/decoders/decodeJPEGLS.ts +++ b/packages/dicom-image-loader/src/shared/decoders/decodeJPEGLS.ts @@ -3,12 +3,15 @@ import charlsFactory from '@cornerstonejs/codec-charls/dist/charlswasm_decode'; // Webpack asset/resource copies this to our output folder import charlsWasm from '@cornerstonejs/codec-charls/dist/charlswasm_decode.wasm'; +import { CornerstoneWadoWebWorkerDecodeConfig } from 'dicom-image-loader/src/imageLoader/webWorkerManager'; +import { ByteArray } from 'dicom-parser'; +import { CornerstoneWadoImageFrame } from '../image-frame'; // import charlsWasm from '@cornerstonejs/codec-charls/dist/debug/charlswasm.wasm'; const local = { codec: undefined, decoder: undefined, - decodeConfig: {}, + decodeConfig: {} as CornerstoneWadoWebWorkerDecodeConfig, }; function getExceptionMessage(exception) { @@ -17,7 +20,9 @@ function getExceptionMessage(exception) { : exception; } -export function initialize(decodeConfig) { +export function initialize( + decodeConfig?: CornerstoneWadoWebWorkerDecodeConfig +): Promise { local.decodeConfig = decodeConfig; if (local.codec) { @@ -49,7 +54,10 @@ export function initialize(decodeConfig) { * @param {object} imageInfo * @param {boolean} imageInfo.signed - (pixelRepresentation === 1) */ -async function decodeAsync(compressedImageFrame, imageInfo) { +async function decodeAsync( + compressedImageFrame, + imageInfo +): Promise { try { await initialize(); const decoder = local.decoder; @@ -113,7 +121,7 @@ async function decodeAsync(compressedImageFrame, imageInfo) { } } -function getPixelData(frameInfo, decodedBuffer, signed) { +function getPixelData(frameInfo, decodedBuffer: ByteArray, signed: boolean) { if (frameInfo.bitsPerSample > 8) { if (signed) { return new Int16Array( diff --git a/packages/dicom-image-loader/src/shared/decoders/decodeJPEGLossless.ts b/packages/dicom-image-loader/src/shared/decoders/decodeJPEGLossless.ts index 016ce6848..92bb3fe5e 100644 --- a/packages/dicom-image-loader/src/shared/decoders/decodeJPEGLossless.ts +++ b/packages/dicom-image-loader/src/shared/decoders/decodeJPEGLossless.ts @@ -1,9 +1,15 @@ +import { CornerstoneWadoWebWorkerDecodeConfig } from 'dicom-image-loader/src/imageLoader/webWorkerManager'; +import { ByteArray } from 'dicom-parser'; +import { CornerstoneWadoImageFrame } from '../image-frame'; + const local = { jpeg: undefined, - decodeConfig: {}, + decodeConfig: {} as CornerstoneWadoWebWorkerDecodeConfig, }; -export function initialize(decodeConfig) { +export function initialize( + decodeConfig?: CornerstoneWadoWebWorkerDecodeConfig +): Promise { local.decodeConfig = decodeConfig; if (local.jpeg) { @@ -18,7 +24,10 @@ export function initialize(decodeConfig) { }); } -async function decodeJPEGLossless(imageFrame, pixelData) { +async function decodeJPEGLossless( + imageFrame: CornerstoneWadoImageFrame, + pixelData: ByteArray +): Promise { await initialize(); // check to make sure codec is loaded diff --git a/packages/dicom-image-loader/src/shared/decoders/decodeLittleEndian.ts b/packages/dicom-image-loader/src/shared/decoders/decodeLittleEndian.ts index 0930d8248..9e3727413 100644 --- a/packages/dicom-image-loader/src/shared/decoders/decodeLittleEndian.ts +++ b/packages/dicom-image-loader/src/shared/decoders/decodeLittleEndian.ts @@ -1,4 +1,10 @@ -async function decodeLittleEndian(imageFrame, pixelData) { +import { ByteArray } from 'dicom-parser'; +import { CornerstoneWadoImageFrame } from '../image-frame'; + +async function decodeLittleEndian( + imageFrame: CornerstoneWadoImageFrame, + pixelData: ByteArray +): Promise { let arrayBuffer = pixelData.buffer; let offset = pixelData.byteOffset; diff --git a/packages/dicom-image-loader/src/shared/decoders/decodeRLE.ts b/packages/dicom-image-loader/src/shared/decoders/decodeRLE.ts index 204bbb965..db622cf6c 100644 --- a/packages/dicom-image-loader/src/shared/decoders/decodeRLE.ts +++ b/packages/dicom-image-loader/src/shared/decoders/decodeRLE.ts @@ -1,4 +1,10 @@ -async function decodeRLE(imageFrame, pixelData) { +import { ByteArray } from 'dicom-parser'; +import { CornerstoneWadoImageFrame } from '../image-frame'; + +async function decodeRLE( + imageFrame: CornerstoneWadoImageFrame, + pixelData: ByteArray +): Promise { if (imageFrame.bitsAllocated === 8) { if (imageFrame.planarConfiguration) { return decode8Planar(imageFrame, pixelData); @@ -12,7 +18,7 @@ async function decodeRLE(imageFrame, pixelData) { throw new Error('unsupported pixel format for RLE'); } -function decode8(imageFrame, pixelData) { +function decode8(imageFrame: CornerstoneWadoImageFrame, pixelData: ByteArray) { const frameData = pixelData; const frameSize = imageFrame.rows * imageFrame.columns; const outFrame = new ArrayBuffer(frameSize * imageFrame.samplesPerPixel); @@ -63,7 +69,10 @@ function decode8(imageFrame, pixelData) { return imageFrame; } -function decode8Planar(imageFrame, pixelData) { +function decode8Planar( + imageFrame: CornerstoneWadoImageFrame, + pixelData: ByteArray +) { const frameData = pixelData; const frameSize = imageFrame.rows * imageFrame.columns; const outFrame = new ArrayBuffer(frameSize * imageFrame.samplesPerPixel); @@ -114,7 +123,7 @@ function decode8Planar(imageFrame, pixelData) { return imageFrame; } -function decode16(imageFrame, pixelData) { +function decode16(imageFrame: CornerstoneWadoImageFrame, pixelData: ByteArray) { const frameData = pixelData; const frameSize = imageFrame.rows * imageFrame.columns; const outFrame = new ArrayBuffer(frameSize * imageFrame.samplesPerPixel * 2); diff --git a/packages/dicom-image-loader/src/shared/getMinMax.ts b/packages/dicom-image-loader/src/shared/getMinMax.ts index a3d727399..17c46148b 100644 --- a/packages/dicom-image-loader/src/shared/getMinMax.ts +++ b/packages/dicom-image-loader/src/shared/getMinMax.ts @@ -1,10 +1,12 @@ +import { ByteArray } from 'dicom-parser'; + /** * Calculate the minimum and maximum values in an Array * * @param {Number[]} storedPixelData * @return {{min: Number, max: Number}} */ -function getMinMax(storedPixelData) { +function getMinMax(storedPixelData: ByteArray): { min: number; max: number } { // we always calculate the min max values since they are not always // present in DICOM and we don't want to trust them anyway as cornerstone // depends on us providing reliable values for these diff --git a/packages/dicom-image-loader/src/shared/image-frame.ts b/packages/dicom-image-loader/src/shared/image-frame.ts new file mode 100644 index 000000000..b9293dec1 --- /dev/null +++ b/packages/dicom-image-loader/src/shared/image-frame.ts @@ -0,0 +1,30 @@ +export interface CornerstoneWadoImageFrame { + samplesPerPixel: number; + photometricInterpretation: string; + planarConfiguration: number; + rows: number; + columns: number; + bitsAllocated: number; + bitsStored: number; + pixelRepresentation: number; + smallestPixelValue: number; + largestPixelValue: number; + redPaletteColorLookupTableDescriptor: number[]; + greenPaletteColorLookupTableDescriptor: number[]; + bluePaletteColorLookupTableDescriptor: number[]; + redPaletteColorLookupTableData: number[]; + greenPaletteColorLookupTableData: number[]; + bluePaletteColorLookupTableData: number[]; + // populated later after decoding + pixelData: + | Float32Array + | Int16Array + | Uint16Array + | Uint8Array + | Uint8ClampedArray + | undefined; + imageData?: ImageData; + decodeTimeInMS?: number; + pixelDataLength?: number; + preScale?: any; +} diff --git a/packages/dicom-image-loader/src/shared/scaling/scaleArray.ts b/packages/dicom-image-loader/src/shared/scaling/scaleArray.ts index 545ef7879..d3f89c0be 100644 --- a/packages/dicom-image-loader/src/shared/scaling/scaleArray.ts +++ b/packages/dicom-image-loader/src/shared/scaling/scaleArray.ts @@ -1,4 +1,7 @@ -export default function scaleArray(array, scalingParameters) { +export default function scaleArray( + array: number[], + scalingParameters +): boolean { const arrayLength = array.length; const { rescaleSlope, rescaleIntercept, suvbw } = scalingParameters; diff --git a/packages/dicom-image-loader/src/shared/types/load-image-options.ts b/packages/dicom-image-loader/src/shared/types/load-image-options.ts new file mode 100644 index 000000000..b5c263b3f --- /dev/null +++ b/packages/dicom-image-loader/src/shared/types/load-image-options.ts @@ -0,0 +1,17 @@ +import { Types } from '@cornerstonejs/core'; +import { CornerstoneWadoLoaderLoadRequestFunction } from 'dicom-image-loader/src/shared/types/load-request-function'; + +export interface CornerstoneLoadImageOptions { + useRGBA?: boolean; + preScale?: { + enabled: boolean; + scalingParameters?: Types.ScalingParameters; + }; + targetBuffer?: { + type: 'Uint8Array' | 'Uint16Array' | 'Float32Array'; + arrayBuffer: ArrayBufferLike; + length: number; + offset: number; + }; + loader?: CornerstoneWadoLoaderLoadRequestFunction; +} diff --git a/packages/dicom-image-loader/src/shared/types/load-request-function.ts b/packages/dicom-image-loader/src/shared/types/load-request-function.ts new file mode 100644 index 000000000..130936f3b --- /dev/null +++ b/packages/dicom-image-loader/src/shared/types/load-request-function.ts @@ -0,0 +1,6 @@ + +export type CornerstoneWadoLoaderLoadRequestFunction = ( + url: string, + imageId: string, + ...args: any[] +) => Promise; diff --git a/packages/dicom-image-loader/src/shared/types/metadata-modules.ts b/packages/dicom-image-loader/src/shared/types/metadata-modules.ts new file mode 100644 index 000000000..207fc190c --- /dev/null +++ b/packages/dicom-image-loader/src/shared/types/metadata-modules.ts @@ -0,0 +1,81 @@ +export type CornerstoneMetaDataTypes = + | 'generalSeriesModule' + | 'patientStudyModule' + | 'imagePlaneModule' + | 'imagePixelModule' + | 'transferSyntax' + | 'sopCommonModule' + | string; + +export interface DicomDateObject { + year: number; + month: number; + day: number; +} + +export interface DicomTimeObject { + hours: number; + minutes?: number; + seconds?: number; + fractionalSeconds?: number; +} + +export interface CornerstoneMetadataGeneralSeriesModule { + modality: string; + seriesInstanceUID: string; + seriesNumber: number; + studyInstanceUID: string; + seriesDate: DicomDateObject; + seriesTime: DicomTimeObject; +} + +export interface CornerstoneMetadataPatientStudyModule { + patientAge: number; + patientSize: number; + patientWeight: number; +} + +export interface CornerstoneMetadataImagePlaneModule { + frameOfReferenceUID: string; + rows: string; + columns: string; + imageOrientationPatient: number[]; + rowCosines: number[]; + columnCosines: number[]; + imagePositionPatient: number[]; + sliceThickness: string; + sliceLocation: string; + pixelSpacing: number[]; + rowPixelSpacing: number | null; + columnPixelSpacing: number | null; +} + +export interface CornerstoneMetadataImagePixelModule { + samplesPerPixel: number; + photometricInterpretation: string; + rows: number; + columns: number; + bitsAllocated: number; + bitsStored: number; + highBit: number; + pixelRepresentation: number; + planarConfiguration: number; + pixelAspectRatio: string; + redPaletteColorLookupTableDescriptor: number[]; + greenPaletteColorLookupTableDescriptor: number[]; + bluePaletteColorLookupTableDescriptor: number[]; + redPaletteColorLookupTableData: number[]; + greenPaletteColorLookupTableData: number[]; + bluePaletteColorLookupTableData: number[]; + smallestPixelValue?: number; + largestPixelValue?: number; +} + +export interface CornerstoneMetadataSopCommonModule { + sopClassUID: string; + sopInstanceUID: string; +} + +export interface CornerstoneMetadataTransferSyntax { + transferSyntaxUID: string; +} diff --git a/packages/dicom-image-loader/src/webWorker/decodeTask.ts b/packages/dicom-image-loader/src/webWorker/decodeTask.ts index 9ade7d214..33918dca7 100644 --- a/packages/dicom-image-loader/src/webWorker/decodeTask.ts +++ b/packages/dicom-image-loader/src/webWorker/decodeTask.ts @@ -1,16 +1,19 @@ -import { initialize as initializeJPEG2000 } from '../shared/decoders/decodeJPEG2000'; -import { initialize as initializeJPEGLS } from '../shared/decoders/decodeJPEGLS'; +import { CornerstoneWadoWebWorkerTaskOptions } from '../imageLoader/webWorkerManager'; import calculateMinMax from '../shared/calculateMinMax'; import decodeImageFrame from '../shared/decodeImageFrame'; +import { initialize as initializeJPEG2000 } from '../shared/decoders/decodeJPEG2000'; +import { initialize as initializeJPEGLS } from '../shared/decoders/decodeJPEGLS'; +import { CornerstoneWadoImageFrame } from '../shared/image-frame'; +import { CornerstoneWadoWebWorkerDecodeData } from './webworker-messages'; // the configuration object for the decodeTask -let decodeConfig; +let decodeConfig: CornerstoneWadoWebWorkerTaskOptions; /** * Function to control loading and initializing the codecs * @param config */ -function loadCodecs(config) { +function loadCodecs(config: CornerstoneWadoWebWorkerTaskOptions) { // Initialize the codecs if (config.decodeTask.initializeCodecsOnStartup) { initializeJPEG2000(config.decodeTask); @@ -21,7 +24,7 @@ function loadCodecs(config) { /** * Task initialization function */ -function initialize(config) { +function initialize(config: CornerstoneWadoWebWorkerTaskOptions): void { decodeConfig = config; loadCodecs(config); @@ -30,7 +33,13 @@ function initialize(config) { /** * Task handler function */ -function handler(data, doneCallback) { +function handler( + data: CornerstoneWadoWebWorkerDecodeData, + doneCallback: ( + imageFrame: CornerstoneWadoImageFrame, + pixelData: Transferable[] + ) => void +): void { // Load the codecs if they aren't already loaded loadCodecs(decodeConfig); @@ -42,7 +51,7 @@ function handler(data, doneCallback) { const pixelData = new Uint8Array(data.data.pixelData); // TODO switch to promise - function finishedCallback(imageFrame) { + function finishedCallback(imageFrame: CornerstoneWadoImageFrame) { if (!imageFrame.pixelData) { throw new Error( 'decodeTask: imageFrame.pixelData is undefined after decoding' @@ -51,9 +60,10 @@ function handler(data, doneCallback) { calculateMinMax(imageFrame, strict); + /** @todo check as any */ // convert from TypedArray to ArrayBuffer since web workers support passing ArrayBuffers but not // typed arrays - imageFrame.pixelData = imageFrame.pixelData.buffer; + imageFrame.pixelData = imageFrame.pixelData.buffer as any; // invoke the callback with our result and pass the pixelData in the transferList to move it to // UI thread without making a copy diff --git a/packages/dicom-image-loader/src/webWorker/webWorker.ts b/packages/dicom-image-loader/src/webWorker/webWorker.ts index fc2bd20ab..2dd64fe21 100644 --- a/packages/dicom-image-loader/src/webWorker/webWorker.ts +++ b/packages/dicom-image-loader/src/webWorker/webWorker.ts @@ -1,17 +1,43 @@ +/// + +import { + CornerstoneWadoWebWorkerOptions, + CornerstoneWadoWebWorkerTaskOptions, + CornerstoneWadoWorkerTaskTypes, +} from '../imageLoader/webWorkerManager'; +import { CornerstoneWadoImageFrame } from '../shared/image-frame'; +import { + CornerstoneWadoWebWorkerData, + CornerstoneWadoWebWorkerDecodeData, + CornerstoneWadoWebWorkerInitializeData, +} from './webworker-messages'; + +interface CornerstoneWadoWebWorkerTaskHandler { + taskType: CornerstoneWadoWorkerTaskTypes; + handler: ( + data: CornerstoneWadoWebWorkerDecodeData, + cb: ( + result: CornerstoneWadoImageFrame, + transferables: Transferable[] + ) => void + ) => void; + initialize: (config: CornerstoneWadoWebWorkerTaskOptions) => void; +} + // an object of task handlers -const taskHandlers = {}; +const taskHandlers: Record = {}; // Flag to ensure web worker is only initialized once let initialized = false; // the configuration object passed in when the web worker manager is initialized -let config; +let config: CornerstoneWadoWebWorkerOptions; /** * Initialization function that loads additional web workers and initializes them * @param data */ -function initialize(data) { +function initialize(data: CornerstoneWadoWebWorkerInitializeData) { // console.log('web worker initialize ', data.workerIndex); // prevent initialization from happening more than once if (initialized) { @@ -21,8 +47,11 @@ function initialize(data) { // save the config data config = data.config; + /** + * @todo review any + */ // Additional web worker tasks can self-register by calling self.registerTaskHandler - self.registerTaskHandler = registerTaskHandler; + (self as any).registerTaskHandler = registerTaskHandler; // load any additional web worker tasks if (data.config.webWorkerTaskPaths) { @@ -51,7 +80,9 @@ function initialize(data) { * Function exposed to web worker tasks to register themselves * @param taskHandler */ -export function registerTaskHandler(taskHandler) { +export function registerTaskHandler( + taskHandler: CornerstoneWadoWebWorkerTaskHandler +): false | void { if (taskHandlers[taskHandler.taskType]) { console.log( 'attempt to register duplicate task handler "', @@ -80,7 +111,7 @@ function loadWebWorkerTask(data) { * Web worker message handler - dispatches messages to the registered task handlers * @param msg */ -self.onmessage = function (msg) { +self.onmessage = function (msg: MessageEvent) { if (!msg.data.taskType) { console.log(msg.data); @@ -120,7 +151,7 @@ self.onmessage = function (msg) { ); } ); - } catch (error) { + } catch (error: any) { console.log(`task ${msg.data.taskType} failed - ${error.message}`); self.postMessage({ taskType: msg.data.taskType, diff --git a/packages/dicom-image-loader/src/webWorker/webworker-messages.ts b/packages/dicom-image-loader/src/webWorker/webworker-messages.ts new file mode 100644 index 000000000..5735dca87 --- /dev/null +++ b/packages/dicom-image-loader/src/webWorker/webworker-messages.ts @@ -0,0 +1,46 @@ +import { ByteArray } from 'dicom-parser'; +import { CornerstoneWadoLoaderOptions } from '../imageLoader/internal/options'; +import { + CornerstoneWadoWebWorkerOptions, + CornerstoneWadoWorkerTaskTypes, +} from '../imageLoader/webWorkerManager'; +import { CornerstoneWadoImageFrame } from '../shared/image-frame'; + +export interface CornerstoneWadoWebWorkerDecodeTaskData { + imageFrame: CornerstoneWadoImageFrame; + transferSyntax: string; + pixelData: ByteArray; + options: CornerstoneWadoLoaderOptions; +} + +export interface CornerstoneWadoWebWorkerDecodeData { + taskType: 'decodeTask'; + workerIndex: number; + data: CornerstoneWadoWebWorkerDecodeTaskData; +} + +export interface CornerstoneWadoWebWorkerLoadData { + taskType: 'loadWebWorkerTask'; + workerIndex: number; + sourcePath: string; + config: CornerstoneWadoWebWorkerOptions; +} + +export interface CornerstoneWadoWebWorkerInitializeData { + taskType: 'initialize'; + workerIndex: number; + config: CornerstoneWadoWebWorkerOptions; +} + +export type CornerstoneWadoWebWorkerData = + | CornerstoneWadoWebWorkerDecodeData + | CornerstoneWadoWebWorkerLoadData + | CornerstoneWadoWebWorkerInitializeData; + +export interface CornerstoneWadoWebWorkerResponse { + taskType: CornerstoneWadoWorkerTaskTypes; + status: 'failed' | 'success'; + workerIndex: number; + data?: CornerstoneWadoImageFrame; + result: string | CornerstoneWadoImageFrame; +} From 09ca22fd29dc527f19ea9e4bd1e9711616d6b686 Mon Sep 17 00:00:00 2001 From: James Manners Date: Wed, 7 Dec 2022 15:27:20 +1100 Subject: [PATCH 25/25] [wip] update types for tests --- packages/dicom-image-loader/package.json | 7 +++++++ .../dicom-image-loader/src/imageLoader/webWorkerManager.ts | 2 +- .../dicom-image-loader/src/shared/calculateMinMax_test.ts | 2 +- packages/dicom-image-loader/src/shared/getMinMax.ts | 5 ++++- 4 files changed, 13 insertions(+), 3 deletions(-) diff --git a/packages/dicom-image-loader/package.json b/packages/dicom-image-loader/package.json index 87b69fc07..131525bf6 100644 --- a/packages/dicom-image-loader/package.json +++ b/packages/dicom-image-loader/package.json @@ -51,6 +51,13 @@ "dicom-parser": "^1.8.9", "pako": "^2.0.4" }, + "devDependencies": { + "@types/chai": "^4.3.4", + "@types/mocha": "^9.1.1", + "chai": "^4.3.4", + "mocha": "^9.1.1" + + }, "contributors": [ { "name": "Cornerstone.js Contributors", diff --git a/packages/dicom-image-loader/src/imageLoader/webWorkerManager.ts b/packages/dicom-image-loader/src/imageLoader/webWorkerManager.ts index 301cd1420..e8814b6c9 100644 --- a/packages/dicom-image-loader/src/imageLoader/webWorkerManager.ts +++ b/packages/dicom-image-loader/src/imageLoader/webWorkerManager.ts @@ -22,7 +22,7 @@ let nextTaskId = 0; export interface CornerstoneWadoWebWorkerDecodeConfig { initializeCodecsOnStartup: boolean; - strict: boolean; + strict?: boolean; } export interface CornerstoneWadoWebWorkerTaskOptions { diff --git a/packages/dicom-image-loader/src/shared/calculateMinMax_test.ts b/packages/dicom-image-loader/src/shared/calculateMinMax_test.ts index 461f79e2a..2f669b341 100644 --- a/packages/dicom-image-loader/src/shared/calculateMinMax_test.ts +++ b/packages/dicom-image-loader/src/shared/calculateMinMax_test.ts @@ -2,7 +2,7 @@ import { expect } from 'chai'; import calculateMinMax from './calculateMinMax'; describe('#calculateMinMax', () => { - let imageFrame = {}; + let imageFrame: any = {}; beforeEach(() => { imageFrame = { diff --git a/packages/dicom-image-loader/src/shared/getMinMax.ts b/packages/dicom-image-loader/src/shared/getMinMax.ts index 17c46148b..02eb142a2 100644 --- a/packages/dicom-image-loader/src/shared/getMinMax.ts +++ b/packages/dicom-image-loader/src/shared/getMinMax.ts @@ -6,7 +6,10 @@ import { ByteArray } from 'dicom-parser'; * @param {Number[]} storedPixelData * @return {{min: Number, max: Number}} */ -function getMinMax(storedPixelData: ByteArray): { min: number; max: number } { +function getMinMax(storedPixelData: ByteArray | number[]): { + min: number; + max: number; +} { // we always calculate the min max values since they are not always // present in DICOM and we don't want to trust them anyway as cornerstone // depends on us providing reliable values for these