From f9e70137b89668b50816ae0f0ca9d0c630951312 Mon Sep 17 00:00:00 2001 From: Leonardo Campos Date: Fri, 20 Oct 2023 08:28:20 -0300 Subject: [PATCH 01/14] feat(colorBar): added color bar component --- packages/core/examples/colorbar/index.ts | 728 ++++++++++++++++++ packages/core/src/index.ts | 3 + packages/core/src/ui/index.ts | 1 + packages/core/src/ui/widgets/Widget.ts | 125 +++ .../core/src/ui/widgets/colorBar/ColorBar.ts | 322 ++++++++ .../src/ui/widgets/colorBar/ColorBarCanvas.ts | 274 +++++++ .../src/ui/widgets/colorBar/ColorBarScale.ts | 398 ++++++++++ .../src/ui/widgets/colorBar/EventListeners.ts | 269 +++++++ .../ui/widgets/colorBar/ViewportColorBar.ts | 173 +++++ .../widgets/colorBar/common/isRangeValid.ts | 7 + .../ui/widgets/colorBar/common/isSizeValid.ts | 7 + .../colorBar/common/positionsEquals.ts | 7 + .../ui/widgets/colorBar/common/rangesEqual.ts | 7 + .../ui/widgets/colorBar/common/sizesEqual.ts | 7 + .../colorBar/enums/ColorBarScalePosition.ts | 4 + .../src/ui/widgets/colorBar/enums/index.ts | 1 + .../core/src/ui/widgets/colorBar/index.ts | 5 + .../colorBar/types/ColorBarCanvasProps.ts | 14 + .../colorBar/types/ColorBarPosition.ts | 4 + .../widgets/colorBar/types/ColorBarProps.ts | 16 + .../widgets/colorBar/types/ColorBarRange.ts | 4 + .../colorBar/types/ColorBarScaleProps.ts | 17 + .../colorBar/types/ColorBarScaleStyle.ts | 8 + .../ui/widgets/colorBar/types/ColorBarSize.ts | 4 + .../colorBar/types/ColorBarVOIRange.ts | 3 + .../src/ui/widgets/colorBar/types/Colormap.ts | 5 + .../colorBar/types/ColormapCanvasProps.ts | 14 + .../src/ui/widgets/colorBar/types/index.ts | 4 + packages/core/src/ui/widgets/index.ts | 1 + utils/demo/helpers/addDropdownToToolbar.ts | 6 + 30 files changed, 2438 insertions(+) create mode 100644 packages/core/examples/colorbar/index.ts create mode 100644 packages/core/src/ui/index.ts create mode 100644 packages/core/src/ui/widgets/Widget.ts create mode 100644 packages/core/src/ui/widgets/colorBar/ColorBar.ts create mode 100644 packages/core/src/ui/widgets/colorBar/ColorBarCanvas.ts create mode 100644 packages/core/src/ui/widgets/colorBar/ColorBarScale.ts create mode 100644 packages/core/src/ui/widgets/colorBar/EventListeners.ts create mode 100644 packages/core/src/ui/widgets/colorBar/ViewportColorBar.ts create mode 100644 packages/core/src/ui/widgets/colorBar/common/isRangeValid.ts create mode 100644 packages/core/src/ui/widgets/colorBar/common/isSizeValid.ts create mode 100644 packages/core/src/ui/widgets/colorBar/common/positionsEquals.ts create mode 100644 packages/core/src/ui/widgets/colorBar/common/rangesEqual.ts create mode 100644 packages/core/src/ui/widgets/colorBar/common/sizesEqual.ts create mode 100644 packages/core/src/ui/widgets/colorBar/enums/ColorBarScalePosition.ts create mode 100644 packages/core/src/ui/widgets/colorBar/enums/index.ts create mode 100644 packages/core/src/ui/widgets/colorBar/index.ts create mode 100644 packages/core/src/ui/widgets/colorBar/types/ColorBarCanvasProps.ts create mode 100644 packages/core/src/ui/widgets/colorBar/types/ColorBarPosition.ts create mode 100644 packages/core/src/ui/widgets/colorBar/types/ColorBarProps.ts create mode 100644 packages/core/src/ui/widgets/colorBar/types/ColorBarRange.ts create mode 100644 packages/core/src/ui/widgets/colorBar/types/ColorBarScaleProps.ts create mode 100644 packages/core/src/ui/widgets/colorBar/types/ColorBarScaleStyle.ts create mode 100644 packages/core/src/ui/widgets/colorBar/types/ColorBarSize.ts create mode 100644 packages/core/src/ui/widgets/colorBar/types/ColorBarVOIRange.ts create mode 100644 packages/core/src/ui/widgets/colorBar/types/Colormap.ts create mode 100644 packages/core/src/ui/widgets/colorBar/types/ColormapCanvasProps.ts create mode 100644 packages/core/src/ui/widgets/colorBar/types/index.ts create mode 100644 packages/core/src/ui/widgets/index.ts diff --git a/packages/core/examples/colorbar/index.ts b/packages/core/examples/colorbar/index.ts new file mode 100644 index 0000000000..6b32a9d2ab --- /dev/null +++ b/packages/core/examples/colorbar/index.ts @@ -0,0 +1,728 @@ +import vtkColormaps from '@kitware/vtk.js/Rendering/Core/ColorTransferFunction/ColorMaps'; +import { + RenderingEngine, + Types, + Enums, + volumeLoader, + getRenderingEngine, + ui, +} from '@cornerstonejs/core'; +import * as cornerstoneTools from '@cornerstonejs/tools'; +import { + initDemo, + createImageIdsAndCacheMetaData, + setTitleAndDescription, + addDropdownToToolbar, + setCtTransferFunctionForVolumeActor, + setPetColorMapTransferFunctionForVolumeActor, +} from '../../../../utils/demo/helpers'; + +const { ViewportColorBar } = ui.widgets.colorbar; +const { ColorBarScalePosition } = ui.widgets.colorbar.Enums; + +// This is for debugging purposes +console.warn( + 'Click on index.ts to open source code for this example --------->' +); + +const { + PanTool, + WindowLevelTool, + StackScrollMouseWheelTool, + ZoomTool, + ToolGroupManager, + Enums: csToolsEnums, +} = cornerstoneTools; + +const { MouseBindings } = csToolsEnums; + +const pause = (interval) => + new Promise((resolve) => setTimeout(resolve, interval)); + +const { ViewportType } = Enums; +const renderingEngineId = 'myRenderingEngine'; +const stackViewportId = 'CT_STACK'; +const volumeViewportId = 'CT_VOLUME_SAGITTAL'; +const volumeToolGroupId = 'VOLUME_TOOL_GROUP_ID'; + +// Define unique ids for the volumes +const volumeLoaderScheme = 'cornerstoneStreamingImageVolume'; // Loader id which defines which volume loader to use +const ctVolumeName = 'CT_VOLUME_ID'; // Id of the volume less loader prefix +const ctVolumeId = `${volumeLoaderScheme}:${ctVolumeName}`; // VolumeId with loader id + volume id + +// Define a unique id for the volume +const ptVolumeName = 'PT_VOLUME_ID'; +const ptVolumeId = `${volumeLoaderScheme}:${ptVolumeName}`; + +const colormaps = vtkColormaps.rgbPresetNames.map( + (presetName) => vtkColormaps.getPresetByName(presetName) as Colormap +); +let currentPTColormapName = 'Black-Body Radiation'; +let ctColorBar = null; +let ptColorBar = null; +const voiRangeMin = 0; +const voiRangeMax = 1; + +// ==[ Set up page ]============================================================ + +setTitleAndDescription( + 'Volume Viewport API With Multiple Volumes', + 'Demonstrates how to interact with a Volume viewport when using fusion.' +); + +const content = document.getElementById('content'); +const element = document.createElement('div'); +element.id = 'cornerstone-element'; + +Object.assign(element.style, { + position: 'relative', + width: '500px', + height: '500px', + marginBottom: '30px', +}); + +content.appendChild(element); + +const rightTopContainer = document.createElement('div'); +const rightBottomContainer = document.createElement('div'); +const bottomLeftContainer = document.createElement('div'); +const bottomRightContainer = document.createElement('div'); + +const containers = [ + rightTopContainer, + rightBottomContainer, + bottomLeftContainer, + bottomRightContainer, +]; + +const info = document.createElement('div'); +content.appendChild(info); + +const addInstruction = (instruction) => { + const node = document.createElement('p'); + node.innerText = instruction; + info.appendChild(node); +}; + +addInstruction('- Select different colormaps'); +addInstruction('- Click and drag on viewport to change VOI'); +addInstruction('- Click and drag at the color bar to change VOI'); + +const colorBarSize = { shortSide: 20, longSide: 250 }; + +// const ctColorBar = new ViewportColorBar({ +// id: 'ctColorBar', +// viewportId: volumeViewportId, +// renderingEngineId, +// colormaps, +// activeColormapName: 'Grayscale', +// // voiRange: { min: 0.25, max: 0.75 }, +// }); + +// const ptColorBar = new ViewportColorBar({ +// id: 'ptColorBar', +// viewportId: volumeViewportId, +// renderingEngineId, +// colormaps, +// activeColormapName: currentPTColormapName, +// // voiRange: { min: 0.25, max: 0.75 }, +// }); + +// const colorBars = [ctColorBar, ptColorBar */]; + +// ctColorBar.rootElement.draggable = true; +// ctColorBar.rootElement.style.cursor = 'move'; + +// ptColorBar.rootElement.draggable = true; +// ptColorBar.rootElement.style.cursor = 'move'; + +Object.assign(bottomLeftContainer.style, { + position: 'absolute', + top: '100%', + left: '0px', + width: `${colorBarSize.longSide}px`, + height: `${colorBarSize.shortSide}px`, +}); + +Object.assign(bottomRightContainer.style, { + position: 'absolute', + top: '100%', + left: '50%', + width: `${colorBarSize.longSide}px`, + height: `${colorBarSize.shortSide}px`, +}); + +Object.assign(rightTopContainer.style, { + position: 'absolute', + top: '0px', + left: '100%', + width: `${colorBarSize.shortSide}px`, + height: `${colorBarSize.longSide}px`, +}); + +Object.assign(rightBottomContainer.style, { + position: 'absolute', + top: '50%', + left: '100%', + width: `${colorBarSize.shortSide}px`, + height: `${colorBarSize.longSide}px`, +}); + +// Change the container style when it has/hasn't a colorbar attached to it +const containersMutationObserver = new MutationObserver((mutations) => { + mutations.forEach((mutation) => { + const container = mutation.target as HTMLElement; + const hasChildNodes = container.hasChildNodes(); + + Object.assign(container.style, { + display: hasChildNodes ? 'block' : 'none', + border: hasChildNodes ? 'solid 1px #555' : 'none', + }); + }); +}); + +containers.forEach((container) => { + const hasChildNodes = container.hasChildNodes(); + + Object.assign(container.style, { + boxSizing: 'border-box', + display: hasChildNodes ? 'block' : 'none', + cursor: 'initial', + }); + + // container.addEventListener('dragover', (evt) => evt.preventDefault()); + // container.addEventListener('drop', (evt: DragEvent) => { + // const target = evt.target as HTMLElement; + // const rawTransferedData = evt.dataTransfer.getData('application/json'); + // const transferedData = JSON.parse(rawTransferedData); + // const colorBar = colorBars.find( + // (colorBar) => colorBar.id === transferedData.colorBarId + // ); + // const sourceContainer = colorBar.rootElement.parentElement; + // const containersSet = new Set(containers); + // let targetContainer = null; + + // // If the element is dropped into the same container the `target` will be the + // // canvas element and we need to search for some parent element that is a container + + // for (let node = target; node !== null; node = node.parentElement) { + // if (containersSet.has(node)) { + // targetContainer = node; + // break; + // } + // } + + // if (!targetContainer) { + // return; + // } + + // const swapColorBars = targetContainer.hasChildNodes(); + + // colorBar.appendTo(targetContainer); + + // if (swapColorBars) { + // const otherColorBar = colorBars.find( + // (colorBar) => colorBar.id !== transferedData.colorBarId + // ); + // otherColorBar.appendTo(sourceContainer); + // } + + // evt.preventDefault(); + // }); + + containersMutationObserver.observe(container, { childList: true }); +}); + +// colorBars.forEach((colorBar) => { +// colorBar.rootElement.addEventListener('dragstart', (evt) => { +// evt.dataTransfer.effectAllowed = 'move'; +// evt.dataTransfer.setData( +// 'application/json', +// JSON.stringify({ +// colorBarId: colorBar.id, +// }) +// ); + +// containers.forEach((container) => +// Object.assign(container.style, { +// display: 'block', +// backgroundColor: 'rgba(0, 255, 0, 0.2)', +// }) +// ); +// }); + +// colorBar.rootElement.addEventListener('dragend', () => { +// containers.forEach( +// (container) => (container.style.backgroundColor = 'none') +// ); + +// containers.forEach((container) => +// Object.assign(container.style, { +// display: container.hasChildNodes() ? 'block' : 'none', +// backgroundColor: 'none', +// }) +// ); +// }); +// }); + +const runTestsButton = document.createElement('button'); + +// runTestsButton.style.marginTop = '20px'; +// runTestsButton.textContent = 'Run dev tests'; +// runTestsButton.addEventListener('click', () => runTests()); +// content.appendChild(runTestsButton); + +// ==[ Toolbar ]================================================================ + +// addButtonToToolbar({ +// title: 'Set CT VOI Range', +// onClick: () => { +// // Get the rendering engine +// const renderingEngine = getRenderingEngine(renderingEngineId); + +// // Get the stack viewport +// const viewport = ( +// renderingEngine.getViewport(volumeViewportId) +// ); + +// viewport.setProperties({ voiRange: { lower: -1500, upper: 2500 } }); +// viewport.render(); +// }, +// }); + +// addButtonToToolbar({ +// title: 'Reset Viewport', +// onClick: () => { +// // Get the rendering engine +// const renderingEngine = getRenderingEngine(renderingEngineId); + +// // Get the volume viewport +// const viewport = ( +// renderingEngine.getViewport(volumeViewportId) +// ); + +// // Resets the viewport's camera +// viewport.resetCamera(); +// // TODO reset the viewport properties, we don't have API for this. + +// viewport.render(); +// }, +// }); + +// const fused = false; + +// addButtonToToolbar({ +// title: 'toggle PET', +// onClick: async () => { +// // Get the rendering engine +// const renderingEngine = getRenderingEngine(renderingEngineId); +// // Get the volume viewport +// const viewport = ( +// renderingEngine.getViewport(volumeViewportId) +// ); +// if (fused) { +// // Removes the PT actor from the scene +// viewport.removeVolumeActors([ptVolumeId], true); +// fused = false; +// } else { +// // Add the PET volume to the viewport. It is in the same DICOM Frame Of Reference/worldspace +// // If it was in a different frame of reference, you would need to register it first. +// await viewport.addVolumes( +// [ +// { +// volumeId: ptVolumeId, +// callback: setPetColorMapTransferFunctionForVolumeActor, +// }, +// ], +// true +// ); +// setPTColormap(currentPTColormapName); +// fused = true; +// } +// }, +// }); + +const orientationOptions = { + axial: 'axial', + sagittal: 'sagittal', + coronal: 'coronal', + oblique: 'oblique', +}; + +addDropdownToToolbar({ + options: { + values: ['axial', 'sagittal', 'coronal', 'oblique'], + defaultValue: 'sagittal', + }, + onSelectedValueChange: (selectedValue) => { + // Get the rendering engine + const renderingEngine = getRenderingEngine(renderingEngineId); + + // Get the volume viewport + const viewport = ( + renderingEngine.getViewport(volumeViewportId) + ); + + let viewUp; + let viewPlaneNormal; + + switch (selectedValue) { + case orientationOptions.axial: + viewport.setOrientation(Enums.OrientationAxis.AXIAL); + + break; + case orientationOptions.sagittal: + viewport.setOrientation(Enums.OrientationAxis.SAGITTAL); + + break; + case orientationOptions.coronal: + viewport.setOrientation(Enums.OrientationAxis.CORONAL); + + break; + case orientationOptions.oblique: + // Some random oblique value for this dataset + viewUp = [-0.5962687530844388, 0.5453181550345819, -0.5891448751239446]; + viewPlaneNormal = [ + -0.5962687530844388, 0.5453181550345819, -0.5891448751239446, + ]; + + viewport.setCamera({ viewUp, viewPlaneNormal }); + viewport.resetCamera(); + break; + } + + viewport.render(); + }, +}); + +addDropdownToToolbar({ + options: { + values: colormaps.map((cm) => cm.Name), + defaultValue: currentPTColormapName, + }, + style: { + maxWidth: '200px', + }, + onSelectedValueChange: (selectedValue) => { + setPTColormap(selectedValue); + }, +}); + +// addSliderToToolbar({ +// title: 'VOI min', +// range: [0, 1], +// step: 0.05, +// defaultValue: voiRangeMin, +// onSelectedValueChange: (value) => { +// voiRangeMin = parseFloat(value); +// // ptColorBar.voiRange = { min: voiRangeMin, max: voiRangeMax }; +// }, +// }); + +// addSliderToToolbar({ +// title: 'VOI max', +// range: [0, 1], +// step: 0.05, +// defaultValue: voiRangeMax, +// onSelectedValueChange: (value) => { +// voiRangeMax = parseFloat(value); +// // ptColorBar.voiRange = { min: voiRangeMin, max: voiRangeMax }; +// }, +// }); + +// ==[ Dev Tests ]============================================================== + +// async function testVoiRange() { +// console.log('Testing VOI range'); + +// const numLoops = 4; +// const numMoves = 60; +// const pauseTime = 1000 / 60; // (1000 / fps) +// const windowWidth = 0.5; + +// for (let numLoop = 0; numLoop < numLoops; numLoop++) { +// const leftToRight = !(numLoop % 2); +// const iStart = numLoop ? 1 : Math.floor(numMoves / 2); +// const iEnd = numLoop === numLoops - 1 ? Math.floor(numMoves / 2) : numMoves; + +// for (let i = iStart; i < iEnd; i++) { +// const position = leftToRight ? i : numMoves - i - 1; +// const windowCenter = position * (1 / (numMoves - 1)); +// const min = windowCenter - windowWidth / 2; +// const max = min + windowWidth; + +// ptColorBar.voiRange = { min, max }; + +// await pause(pauseTime); +// } +// } + +// // Restore voiRange to max +// ptColorBar.voiRange = { min: 0, max: 1 }; +// } + +// // Tests to make sure it works when resizing the container or attaching to a new container +// async function testContainers() { +// await pause(500); + +// console.log('append to right containers'); +// ctColorBar.appendTo(rightTopContainer); +// ptColorBar.appendTo(rightBottomContainer); + +// await pause(500); + +// console.log('update right containers size'); +// Object.assign(rightTopContainer.style, { +// height: `${colorBarSize.longSide - 100}px`, +// }); +// Object.assign(rightBottomContainer.style, { +// height: `${colorBarSize.longSide - 100}px`, +// }); + +// await pause(500); + +// console.log('append to bottom containers'); +// ctColorBar.appendTo(bottomLeftContainer); +// ptColorBar.appendTo(bottomRightContainer); + +// // Restore right containers size +// Object.assign(rightTopContainer.style, { +// height: `${colorBarSize.longSide}px`, +// }); +// Object.assign(rightBottomContainer.style, { +// height: `${colorBarSize.longSide}px`, +// }); + +// await pause(500); + +// console.log('update bottom containers size'); +// Object.assign(bottomLeftContainer.style, { +// width: `${colorBarSize.longSide - 100}px`, +// }); +// Object.assign(bottomRightContainer.style, { +// width: `${colorBarSize.longSide - 100}px`, +// }); + +// await pause(500); + +// // Restore bottom containers size +// Object.assign(bottomLeftContainer.style, { +// width: `${colorBarSize.longSide}px`, +// }); +// Object.assign(bottomRightContainer.style, { +// width: `${colorBarSize.longSide}px`, +// }); +// } + +// async function testPTColormaps() { +// for (let i = 1, len = colormaps.length; i < len; i++) { +// console.log(`Colormap: ${colormaps[i].Name}`); +// ptColorBar.activeColormapName = colormaps[i].Name; +// await pause(100); +// } + +// // Back to the first colormap +// console.log(`Colormap: ${colormaps[0].Name}`); +// ptColorBar.activeColormapName = colormaps[0].Name; +// } + +async function runTests() { + // const currentCTParent = ctColorBar.rootElement.parentElement; + // const currentPTParent = ptColorBar.rootElement.parentElement; + // console.log('Dev tests started'); + // await testContainers(); + // // Add them back to theirs parents + // ctColorBar.appendTo(currentCTParent); + // ptColorBar.appendTo(currentPTParent); + // await testPTColormaps(); + // await testVoiRange(); + // console.log('Dev tests complete'); +} + +// ============================================================================= + +function setPTColormap(colormapName: string) { + currentPTColormapName = colormapName; + + if (ptColorBar) { + ptColorBar.activeColormapName = colormapName; + } + + // Get the rendering engine + const renderingEngine = getRenderingEngine(renderingEngineId); + + // Get the volume viewport + const viewport = ( + renderingEngine.getViewport(volumeViewportId) + ); + + viewport.setProperties({ colormap: { name: colormapName } }, ptVolumeId); + viewport.render(); +} + +/** + * Runs the demo + */ +async function run() { + // Init Cornerstone and related libraries + await initDemo(); + + // Add tools to Cornerstone3D + cornerstoneTools.addTool(PanTool); + cornerstoneTools.addTool(WindowLevelTool); + cornerstoneTools.addTool(StackScrollMouseWheelTool); + cornerstoneTools.addTool(ZoomTool); + + // Define a tool group, which defines how mouse events map to tool commands for + // Any viewport using the group + const volumeToolGroup = ToolGroupManager.createToolGroup(volumeToolGroupId); + + // Add tools to the tool group + volumeToolGroup.addTool(WindowLevelTool.toolName); + volumeToolGroup.addTool(PanTool.toolName); + volumeToolGroup.addTool(ZoomTool.toolName); + volumeToolGroup.addTool(StackScrollMouseWheelTool.toolName); + + // Set the initial state of the tools, here all tools are active and bound to + // Different mouse inputs + volumeToolGroup.setToolActive(WindowLevelTool.toolName, { + bindings: [ + { + mouseButton: MouseBindings.Primary, // Left Click + }, + ], + }); + + volumeToolGroup.setToolActive(PanTool.toolName, { + bindings: [ + { + mouseButton: MouseBindings.Auxiliary, // Middle Click + }, + ], + }); + + volumeToolGroup.setToolActive(ZoomTool.toolName, { + bindings: [ + { + mouseButton: MouseBindings.Secondary, // Right Click + }, + ], + }); + + // 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. + volumeToolGroup.setToolActive(StackScrollMouseWheelTool.toolName); + + const wadoRsRoot = 'https://d3t6nz73ql33tx.cloudfront.net/dicomweb'; + const StudyInstanceUID = + '1.3.6.1.4.1.14519.5.2.1.7009.2403.334240657131972136850343327463'; + + // Get Cornerstone imageIds and fetch metadata into RAM + const ctImageIds = await createImageIdsAndCacheMetaData({ + StudyInstanceUID, + SeriesInstanceUID: + '1.3.6.1.4.1.14519.5.2.1.7009.2403.226151125820845824875394858561', + wadoRsRoot, + }); + + const ptImageIds = await createImageIdsAndCacheMetaData({ + StudyInstanceUID, + SeriesInstanceUID: + '1.3.6.1.4.1.14519.5.2.1.7009.2403.879445243400782656317561081015', + wadoRsRoot, + }); + + // Instantiate a rendering engine + const renderingEngine = new RenderingEngine(renderingEngineId); + + // Create a stack viewport + const viewportInput = { + viewportId: volumeViewportId, + type: ViewportType.ORTHOGRAPHIC, + element, + defaultOptions: { + orientation: Enums.OrientationAxis.SAGITTAL, + background: [0.2, 0, 0.2], + }, + }; + + renderingEngine.enableElement(viewportInput); + + // Set the tool group on the viewport + volumeToolGroup.addViewport(volumeViewportId, renderingEngineId); + + // Get the stack viewport that was created + const viewport = ( + renderingEngine.getViewport(volumeViewportId) + ); + + // Define a volume in memory + const ctVolume = await volumeLoader.createAndCacheVolume(ctVolumeId, { + imageIds: ctImageIds, + }); + + // Set the volume to load + ctVolume.load(); + + // Define a volume in memory + const ptVolume = await volumeLoader.createAndCacheVolume(ptVolumeId, { + imageIds: ptImageIds, + }); + + // Set the volume to load + ptVolume.load(); + + // Set the volume on the viewport + await viewport.setVolumes([ + { volumeId: ctVolumeId, callback: setCtTransferFunctionForVolumeActor }, + { + volumeId: ptVolumeId, + callback: setPetColorMapTransferFunctionForVolumeActor, + }, + ]); + + setPTColormap(currentPTColormapName); + + // Render the image + renderingEngine.render(); + + // Append the containers after initializing the viewport to keep them over + // all other viewport elements + element.appendChild(rightTopContainer); + element.appendChild(rightBottomContainer); + element.appendChild(bottomLeftContainer); + element.appendChild(bottomRightContainer); + + const scaleStyle = { + font: '12px Arial', + color: '#fff', + maxNumTicks: 8, + tickSize: 5, + tickWidth: 1, + labelMargin: 3, + }; + + ctColorBar = new ViewportColorBar({ + id: 'ctColorBar', + element, + container: rightTopContainer, + volumeId: ctVolumeId, + colormaps, + activeColormapName: 'Grayscale', + scalePosition: ColorBarScalePosition.TopOrLeft, + scaleStyle, + }); + + ptColorBar = new ViewportColorBar({ + id: 'ptColorBar', + element, + container: rightBottomContainer, + volumeId: ptVolumeId, + colormaps, + activeColormapName: currentPTColormapName, + scalePosition: ColorBarScalePosition.TopOrLeft, + scaleStyle, + }); +} + +run(); diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index fb9614ec42..be46110de6 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -50,6 +50,7 @@ import * as imageLoader from './loaders/imageLoader'; import * as geometryLoader from './loaders/geometryLoader'; import * as Types from './types'; import * as utilities from './utilities'; +import * as ui from './ui'; import { registerImageLoader } from './loaders/imageLoader'; // since it is used by CSWIL right now import triggerEvent from './utilities/triggerEvent'; @@ -122,4 +123,6 @@ export { resetUseSharedArrayBuffer, // Geometry Loader geometryLoader, + // User Interface + ui, }; diff --git a/packages/core/src/ui/index.ts b/packages/core/src/ui/index.ts new file mode 100644 index 0000000000..0a512415b7 --- /dev/null +++ b/packages/core/src/ui/index.ts @@ -0,0 +1 @@ +export * as widgets from './widgets'; diff --git a/packages/core/src/ui/widgets/Widget.ts b/packages/core/src/ui/widgets/Widget.ts new file mode 100644 index 0000000000..d619b44d41 --- /dev/null +++ b/packages/core/src/ui/widgets/Widget.ts @@ -0,0 +1,125 @@ +export type WidgetProps = { + id: string; + container?: HTMLElement; +}; + +type WidgetSize = { + width: number; + height: number; +}; + +class Widget { + private _id: string; + private _rootElement: HTMLElement; + private _containerSize: WidgetSize; + private _containerResizeObserver: ResizeObserver; + + constructor({ id, container }: WidgetProps) { + this._id = id; + this._containerSize = { width: 0, height: 0 }; + this._rootElement = this.createRootElement(); + this._containerResizeObserver = new ResizeObserver( + this._containerResizeCallback + ); + + if (container) { + this.appendTo(container); + } + } + + /** + * Widget id + */ + public get id() { + return this._id; + } + + public get rootElement(): HTMLElement { + return this._rootElement; + } + + protected createRootElement(): HTMLElement { + const rootElement = document.createElement('div'); + + Object.assign(rootElement.style, { + width: '100%', + height: '100%', + }); + + return rootElement; + } + + /** + * Append the color bar node to a parent element and re-renders the color bar + * @param container - HTML element where the color bar will be added to + */ + public appendTo(container: HTMLElement) { + const { + _rootElement: rootElement, + _containerResizeObserver: resizeObserver, + } = this; + const { parentElement: currentContainer } = rootElement; + + if (!container || container === currentContainer) { + return; + } + + if (currentContainer) { + resizeObserver.unobserve(currentContainer); + } + + container.appendChild(rootElement); + resizeObserver.observe(container); + } + + protected get containerSize(): WidgetSize { + // Returns a copy to prevent any external change + return { ...this._containerSize }; + } + + /** + * Method called every time widget's container is resize giving the + * opportunity to children classes to act when that happens. + */ + protected containerResized() { + // no-op + } + + private _containerResizeCallback = (entries: ResizeObserverEntry[]): void => { + let width; + let height; + + const { contentRect, contentBoxSize } = entries[0]; + + // `contentRect` is better supported than `borderBoxSize` or `contentBoxSize`, + // but it is left over from an earlier implementation of the Resize Observer API + // and may be deprecated in future versions. + // https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserverEntry/contentRect + if (contentRect) { + width = contentRect.width; + height = contentRect.height; + } else if (contentBoxSize?.length) { + width = contentBoxSize[0].inlineSize; + height = contentBoxSize[0].blockSize; + } + + this._containerSize = { width, height }; + this.containerResized(); + }; + + /** + * Removes the widget from the DOM and stop listening to DOM events + */ + public dispose() { + const { + _rootElement: rootElement, + _containerResizeObserver: resizeObserver, + } = this; + const { parentElement } = rootElement; + + parentElement?.removeChild(rootElement); + resizeObserver.disconnect(); + } +} + +export { Widget as default, Widget }; diff --git a/packages/core/src/ui/widgets/colorBar/ColorBar.ts b/packages/core/src/ui/widgets/colorBar/ColorBar.ts new file mode 100644 index 0000000000..e4704697b6 --- /dev/null +++ b/packages/core/src/ui/widgets/colorBar/ColorBar.ts @@ -0,0 +1,322 @@ +import { vec2 } from 'gl-matrix'; +import { utilities, Types } from '@cornerstonejs/core'; +import { EventListenersManager } from './EventListeners'; +import { Widget } from '../Widget'; +import { ColorBarProps, ColorBarVOIRange, Colormap } from './types'; +import ColorBarCanvas from './ColorBarCanvas'; +import ColorBarScale from './ColorBarScale'; +import isRangeValid from './common/isRangeValid'; +import rangesEqual from './common/rangesEqual'; +import { ColorBarScalePosition } from './enums/ColorBarScalePosition'; + +const DEFAULT_MULTIPLIER = 1; +const DEFAULT_SCALE_BAR_POSITION = ColorBarScalePosition.BottomOrRight; +const SCALE_BAR_SIZE = 50; + +type ColorBarPoints = { + page: Types.Point2; + client: Types.Point2; + local: Types.Point2; +}; + +class ColorBar extends Widget { + private _colormaps: Map; + private _activeColormapName: string; + private _eventListenersManager: EventListenersManager; + private _canvas: ColorBarCanvas; + private _scaleBar: ColorBarScale; + private _scalePosition: ColorBarScalePosition; + private _isInteracting = false; + + constructor(props: ColorBarProps) { + super(props); + + this._eventListenersManager = new EventListenersManager(); + this._colormaps = ColorBar.getColormapsMap(props); + this._activeColormapName = ColorBar.getInitialColormapName(props); + this._canvas = this._createCanvas(props); + this._scaleBar = this._createScaleBar(props); + this._scalePosition = props.scalePosition ?? DEFAULT_SCALE_BAR_POSITION; + + this._canvas.appendTo(this.rootElement); + this._scaleBar.appendTo(document.body); + + this._addRootElementEventListeners(); + } + + private static getColormapsMap(props: ColorBarProps) { + const { colormaps } = props; + + return colormaps.reduce( + (items, item) => items.set(item.Name, item), + new Map() + ); + } + + private static getInitialColormapName(props: ColorBarProps) { + const { activeColormapName, colormaps } = props; + const colormapExists = + !!activeColormapName && + colormaps.some((cm) => cm.Name === activeColormapName); + + return colormapExists ? activeColormapName : colormaps[0].Name; + } + + /** + * Returns the active LUT name + */ + public get activeColormapName() { + return this._activeColormapName; + } + + /** + * Set the current active LUT name and re-renders the color bar + */ + public set activeColormapName(colormapName: string) { + if (colormapName === this._activeColormapName) { + return; + } + + const colormap = this._colormaps.get(colormapName); + + if (!colormap) { + console.warn(`Invalid colormap name (${colormapName})`); + return; + } + + this._activeColormapName = colormapName; + this._canvas.colormap = colormap; + } + + public get range() { + return this._canvas.range; + } + + public set range(range: ColorBarVOIRange) { + this._canvas.range = range; + this._scaleBar.range = range; + } + + public get voiRange() { + return this._canvas.voiRange; + } + + public set voiRange(voiRange: ColorBarVOIRange) { + const { voiRange: currentVoiRange } = this._canvas; + + if (!isRangeValid(voiRange) || rangesEqual(voiRange, currentVoiRange)) { + return; + } + + this._canvas.voiRange = voiRange; + this._scaleBar.voiRange = voiRange; + this.voiChanged(voiRange); + } + + public get showFullPixelValueRange() { + return this._canvas.showFullPixelValueRange; + } + + public set showFullPixelValueRange(value: boolean) { + this._canvas.showFullPixelValueRange = value; + this._scaleBar.showFullPixelValueRange = value; + } + + public dispose() { + super.dispose(); + this._removeRootElementEventListeners(); + } + + protected createRootElement(): HTMLElement { + const rootElement = document.createElement('div'); + + Object.assign(rootElement.style, { + width: '100%', + height: '100%', + }); + + return rootElement; + } + + protected containerResized() { + super.containerResized(); + this._canvas.size = this.containerSize; + } + + protected getVOIMultipliers(): [number, number] { + return [DEFAULT_MULTIPLIER, DEFAULT_MULTIPLIER]; + } + + protected voiChanged(voiRange: ColorBarVOIRange) { + // TODO: override voiRange property? + } + + private _createCanvas(props: ColorBarProps) { + const { range, voiRange, showFullPixelValueRange } = props; + const colormap = this._colormaps.get(this._activeColormapName); + + return new ColorBarCanvas({ + colormap, + range: range, + voiRange: voiRange, + showFullPixelValueRange, + }); + } + + public _createScaleBar(props: ColorBarProps): ColorBarScale { + return new ColorBarScale({ + range: props.range, + voiRange: props.voiRange, + scaleStyle: props.scaleStyle, + scalePosition: props.scalePosition, + showFullPixelValueRange: props.showFullPixelValueRange, + }); + } + + private _getPointsFromMouseEvent(evt: MouseEvent): ColorBarPoints { + const { rootElement: element } = this; + const clientPoint: Types.Point2 = [evt.clientX, evt.clientY]; + const pagePoint: Types.Point2 = [evt.pageX, evt.pageY]; + const rect = element.getBoundingClientRect(); + const localPoints: Types.Point2 = [ + pagePoint[0] - rect.left - window.pageXOffset, + pagePoint[1] - rect.top - window.pageYOffset, + ]; + + return { client: clientPoint, page: pagePoint, local: localPoints }; + } + + private showScaleBar() { + const { _scaleBar: scaleBar } = this; + const { width: containerWidth, height: containerHeight } = + this.containerSize; + const { top: containerTop, left: containerLeft } = + this.rootElement.getBoundingClientRect(); + const isHorizontal = containerWidth >= containerHeight; + const width = isHorizontal ? containerWidth : SCALE_BAR_SIZE; + const height = isHorizontal ? SCALE_BAR_SIZE : containerHeight; + + let scaleBarTop; + let scaleBarLeft; + + scaleBar.size = { width, height }; + + if (isHorizontal) { + scaleBarTop = + this._scalePosition === ColorBarScalePosition.TopOrLeft + ? containerTop - height + : containerTop + containerHeight; + + scaleBarLeft = containerLeft; + } else { + scaleBarTop = containerTop; + + scaleBarLeft = + this._scalePosition === ColorBarScalePosition.TopOrLeft + ? containerLeft - width + : containerLeft + containerWidth; + } + + scaleBar.position = { top: scaleBarTop, left: scaleBarLeft }; + scaleBar.visible = true; + } + + private _mouseOverCallback = (evt) => { + this.showScaleBar(); + evt.stopPropagation(); + }; + + private _mouseOutCallback = (evt) => { + if (!this._isInteracting) { + this._scaleBar.visible = false; + } + evt.stopPropagation(); + }; + + private _mouseDownCallback = (evt: MouseEvent) => { + this._isInteracting = true; + this._addVOIEventListeners(evt); + evt.stopPropagation(); + }; + + private _mouseDragCallback = (evt, initialState) => { + const multipliers = this.getVOIMultipliers(); + const currentPoints = this._getPointsFromMouseEvent(evt); + const { points: startPoints, voiRange: startVOIRange } = initialState; + const canvasDelta = vec2.sub( + vec2.create(), + currentPoints.local, + startPoints.local + ); + + const wwDelta = canvasDelta[0] * multipliers[0]; + const wcDelta = canvasDelta[1] * multipliers[1]; + + if (!wwDelta && !wcDelta) { + return; + } + + const { lower: voiLower, upper: voiUpper } = startVOIRange; + let { windowWidth, windowCenter } = utilities.windowLevel.toWindowLevel( + voiLower, + voiUpper + ); + + windowWidth = Math.max(windowWidth + wwDelta, 1); + windowCenter += wcDelta; + + const newVoiRange = utilities.windowLevel.toLowHighRange( + windowWidth, + windowCenter + ); + + this.voiRange = newVoiRange; + evt.stopPropagation(); + }; + + private _mouseUpCallback = (evt) => { + this._isInteracting = false; + this._removeVOIEventListeners(); + evt.stopPropagation(); + }; + + private _addRootElementEventListeners() { + const { rootElement: element } = this; + + this._removeRootElementEventListeners(); + element.addEventListener('mouseover', this._mouseOverCallback); + element.addEventListener('mouseout', this._mouseOutCallback); + element.addEventListener('mousedown', this._mouseDownCallback); + } + + private _removeRootElementEventListeners() { + const { rootElement: element } = this; + + element.removeEventListener('mouseover', this._mouseOverCallback); + element.removeEventListener('mouseout', this._mouseOutCallback); + element.removeEventListener('mousedown', this._mouseDownCallback); + } + + private _addVOIEventListeners(evt: MouseEvent) { + const { _eventListenersManager: manager } = this; + const points = this._getPointsFromMouseEvent(evt); + const voiRange = { ...this._canvas.voiRange }; + const initialDragState = { points, voiRange }; + + this._removeVOIEventListeners(); + + document.addEventListener('mouseup', this._mouseUpCallback); + manager.addEventListener(document, 'colorbar.voi.mousemove', (evt) => + this._mouseDragCallback(evt, initialDragState) + ); + } + + private _removeVOIEventListeners() { + const { _eventListenersManager: manager } = this; + + document.removeEventListener('mouseup', this._mouseUpCallback); + manager.removeEventListener(document, 'colorbar.voi.mousemove'); + } +} + +export { ColorBar as default, ColorBar }; diff --git a/packages/core/src/ui/widgets/colorBar/ColorBarCanvas.ts b/packages/core/src/ui/widgets/colorBar/ColorBarCanvas.ts new file mode 100644 index 0000000000..e2d0bda887 --- /dev/null +++ b/packages/core/src/ui/widgets/colorBar/ColorBarCanvas.ts @@ -0,0 +1,274 @@ +import { utilities } from '@cornerstonejs/core'; +import { ColorBarRange, ColorBarVOIRange, Colormap } from './types'; +import { ColorBarCanvasProps } from './types/ColormapCanvasProps'; +import { ColorBarSize } from './types/ColorBarSize'; +import isRangeValid from './common/isRangeValid'; +import rangesEqual from './common/rangesEqual'; +import isSizeValid from './common/isSizeValid'; +import sizesEqual from './common/sizesEqual'; + +const clamp = (value, min, max) => Math.min(Math.max(min, value), max); + +const interpolateVec3 = (a, b, t) => { + return [ + a[0] * (1 - t) + b[0] * t, + a[1] * (1 - t) + b[1] * t, + a[2] * (1 - t) + b[2] * t, + ]; +}; + +class ColorBarCanvas { + private _canvas: HTMLCanvasElement; + private _range: ColorBarRange; + private _voiRange: ColorBarVOIRange; + private _colormap: Colormap; + private _showFullPixelValueRange: boolean; + + constructor(props: ColorBarCanvasProps) { + ColorBarCanvas.validateProps(props); + + const { + colormap, + size = { width: 20, height: 100 }, + range = { lower: 0, upper: 1 }, + voiRange = { lower: 0, upper: 1 }, + container, + showFullPixelValueRange = false, + } = props; + + this._colormap = colormap; + this._range = range; + this._voiRange = voiRange; + this._showFullPixelValueRange = showFullPixelValueRange; + this._canvas = this._createRootElement(size); + + if (container) { + this.appendTo(container); + } + } + + public get colormap(): Colormap { + return this._colormap; + } + + public set colormap(colormap: Colormap) { + this._colormap = colormap; + this.render(); + } + + public get size(): ColorBarSize { + const { width, height } = this._canvas; + return { width, height }; + } + + public set size(size: ColorBarSize) { + const { _canvas: canvas } = this; + + if (!isSizeValid(size) || sizesEqual(canvas, size)) { + return; + } + + this._setCanvasSize(canvas, size); + this.render(); + } + + public get range(): ColorBarRange { + return { ...this._range }; + } + + public set range(range: ColorBarRange) { + if (!isRangeValid(range) || rangesEqual(range, this._range)) { + return; + } + + this._range = range; + this.render(); + } + + public get voiRange(): ColorBarVOIRange { + return { ...this._voiRange }; + } + + public set voiRange(voiRange: ColorBarVOIRange) { + if (!isRangeValid(voiRange) || rangesEqual(voiRange, this._voiRange)) { + return; + } + + this._voiRange = voiRange; + this.render(); + } + + public get showFullPixelValueRange(): boolean { + return this._showFullPixelValueRange; + } + + public set showFullPixelValueRange(showFullRange: boolean) { + if (showFullRange === this._showFullPixelValueRange) { + return; + } + + this._showFullPixelValueRange = showFullRange; + this.render(); + } + + public appendTo(container: HTMLElement) { + container.appendChild(this._canvas); + this.render(); + } + + public dispose() { + const { _canvas: canvas } = this; + const { parentElement } = canvas; + + parentElement?.removeChild(canvas); + } + + private static validateProps(props: ColorBarCanvasProps) { + const { size, range, voiRange } = props; + + if (size && !isSizeValid(size)) { + throw new Error('Invalid "size"'); + } + + if (range && !isRangeValid(range)) { + throw new Error('Invalid "range"'); + } + + if (voiRange && !isRangeValid(voiRange)) { + throw new Error('Invalid "voiRange"'); + } + } + + private _setCanvasSize(canvas: HTMLCanvasElement, size: ColorBarSize) { + const { width, height } = size; + + canvas.width = width; + canvas.height = height; + + Object.assign(canvas.style, { + width: `${width}px`, + height: `${height}px`, + }); + } + + private _createRootElement(size: ColorBarSize) { + const canvas = document.createElement('canvas'); + + Object.assign(canvas.style, { + pointerEvents: 'none', + boxSizing: 'border-box', + }); + + this._setCanvasSize(canvas, size); + + return canvas; + } + + private render(): void { + if (!this._canvas.isConnected) { + return; + } + + const { _colormap: colormap } = this; + const { RGBPoints: rgbPoints } = colormap; + const colorsCount = rgbPoints.length / 4; + + const getColorPoint = (index) => { + const offset = 4 * index; + + if (index < 0 || index >= colorsCount) { + return; + } + + return { + index, + position: rgbPoints[offset], + color: [ + rgbPoints[offset + 1], + rgbPoints[offset + 2], + rgbPoints[offset + 3], + ], + }; + }; + + const { width, height } = this._canvas; + const canvasContext = this._canvas.getContext('2d'); + const isHorizontal = width > height; + const maxValue = isHorizontal ? width : height; + const { _voiRange: voiRange } = this; + const range = this._showFullPixelValueRange ? this._range : { ...voiRange }; + + const { windowWidth } = utilities.windowLevel.toWindowLevel( + voiRange.lower, + voiRange.upper + ); + + let previousColorPoint = undefined; + let currentColorPoint = getColorPoint(0); + + const incRawPixelValue = (range.upper - range.lower) / (maxValue - 1); + let rawPixelValue = range.lower; + + for (let i = 0; i < maxValue; i++) { + const tVoiRange = (rawPixelValue - voiRange.lower) / windowWidth; + + // Find the color in a linear way (O(n) complexity). + // currentColorPoint shall move to the next color until tVoiRange is smaller + // than or equal to next color position. + if (currentColorPoint) { + for (let i = currentColorPoint.index; i < colorsCount; i++) { + if (tVoiRange <= currentColorPoint.position) { + break; + } + + previousColorPoint = currentColorPoint; + currentColorPoint = getColorPoint(i + 1); + } + } + + let normColor; + + // For: + // - firstColorPoint = getColorPoint(0) + // - secondColorPoint = getColorPoint(1) + // - lastColorPoint = getColorPoint(colorsCount - 1) + // Then + // - previousColorPoint shall be undefined when tVoiRange < firstColorPoint.position + // - currentColorPoint shall be undefined when tVoiRange > lastColorPoint.position + // - previousColorPoint and currentColorPoint will be defined when + // currentColorPoint.position is between secondColorPoint.position and + // lastColorPoint.position. + if (!previousColorPoint) { + normColor = [...currentColorPoint.color]; + } else if (!currentColorPoint) { + normColor = [...previousColorPoint.color]; + } else { + const tColorRange = + (tVoiRange - previousColorPoint.position) / + (currentColorPoint.position - previousColorPoint.position); + + normColor = interpolateVec3( + previousColorPoint.color, + currentColorPoint.color, + tColorRange + ); + } + + const color = normColor.map((color) => + clamp(Math.round(color * 255), 0, 255) + ); + + canvasContext.fillStyle = `rgb(${color[0]}, ${color[1]}, ${color[2]})`; + + if (isHorizontal) { + canvasContext.fillRect(i, 0, 1, height); + } else { + canvasContext.fillRect(0, height - i - 1, width, 1); + } + + rawPixelValue += incRawPixelValue; + } + } +} + +export { ColorBarCanvas as default, ColorBarCanvas }; diff --git a/packages/core/src/ui/widgets/colorBar/ColorBarScale.ts b/packages/core/src/ui/widgets/colorBar/ColorBarScale.ts new file mode 100644 index 0000000000..a93a01455c --- /dev/null +++ b/packages/core/src/ui/widgets/colorBar/ColorBarScale.ts @@ -0,0 +1,398 @@ +import { ColorBarRange } from './types/ColorBarRange'; +import { ColorBarVOIRange } from './types/ColorBarVOIRange'; +import { ColorBarSize } from './types/ColorBarSize'; +import { ColorBarScaleProps } from './types/ColorBarScaleProps'; +import { ColorBarPosition } from './types/ColorBarPosition'; +import isSizeValid from './common/isSizeValid'; +import isRangeValid from './common/isRangeValid'; +import rangesEqual from './common/rangesEqual'; +import sizesEqual from './common/sizesEqual'; +import positionsEqual from './common/positionsEquals'; +import { ColorBarScalePosition } from './enums/ColorBarScalePosition'; + +const DEFAULT_FONT = '10px Arial'; +const DEFAULT_COLOR = 'white'; +const DEFAULT_TICK_SIZE = 5; +const DEFAULT_TICK_WIDTH = 1; +const DEFAULT_TICK_LABEL_MARGIN = 3; +const DEFAULT_MAX_NUM_TICKS = 8; + +class ColorBarScale { + private _canvas: HTMLCanvasElement; + private _range: ColorBarRange; + private _voiRange: ColorBarVOIRange; + private _color: string; + private _tickSize: number; + private _tickWidth: number; + private _labelMargin: number; + private _maxNumTicks: number; + private _scalePosition: ColorBarScalePosition; + private _showFullPixelValueRange: boolean; + private _font: string; + + constructor(props: ColorBarScaleProps) { + ColorBarScale.validateProps(props); + + const { + size = { width: 20, height: 100 }, + position = { top: 0, left: 0 }, + range = { lower: 0, upper: 1 }, + voiRange = { lower: 0, upper: 1 }, + scaleStyle, + scalePosition, + container, + showFullPixelValueRange = false, + } = props; + + this._range = range; + this._voiRange = voiRange; + this._font = scaleStyle?.font ?? DEFAULT_FONT; + this._color = scaleStyle?.color ?? DEFAULT_COLOR; + this._tickSize = scaleStyle?.tickSize ?? DEFAULT_TICK_SIZE; + this._tickWidth = scaleStyle?.tickWidth ?? DEFAULT_TICK_WIDTH; + this._labelMargin = scaleStyle?.labelMargin ?? DEFAULT_TICK_LABEL_MARGIN; + this._maxNumTicks = scaleStyle?.maxNumTicks ?? DEFAULT_MAX_NUM_TICKS; + this._scalePosition = scalePosition ?? ColorBarScalePosition.TopOrLeft; + this._showFullPixelValueRange = showFullPixelValueRange; + this._canvas = this._createCanvasElement(size, position); + + if (container) { + this.appendTo(container); + } + } + + public get size(): ColorBarSize { + const { width, height } = this._canvas; + return { width, height }; + } + + public set size(size: ColorBarSize) { + const { _canvas: canvas } = this; + + if (!isSizeValid(size) || sizesEqual(canvas, size)) { + return; + } + + this._setCanvasSize(canvas, size); + this.render(); + } + + public get position(): ColorBarPosition { + return this._getCanvasPosition(this._canvas); + } + + public set position(position: ColorBarPosition) { + const { _canvas: canvas } = this; + const currentPosition = this._getCanvasPosition(canvas); + + if (positionsEqual(position, currentPosition)) { + return; + } + + this._setCanvasPosition(canvas, position); + this.render(); + } + + public get range() { + return { ...this._range }; + } + + public set range(range: ColorBarVOIRange) { + if (!isRangeValid(range) || rangesEqual(range, this._range)) { + return; + } + + this._range = range; + this.render(); + } + + public get voiRange() { + return { ...this._voiRange }; + } + + public set voiRange(voiRange: ColorBarVOIRange) { + if (!isRangeValid(voiRange) || rangesEqual(voiRange, this._voiRange)) { + return; + } + + this._voiRange = voiRange; + this.render(); + } + + public get tickSize(): number { + return this._tickSize; + } + + public set tickSize(tickSize: number) { + if (tickSize === this._tickSize) { + return; + } + + this._tickSize = tickSize; + this.render(); + } + + public get tickWidth(): number { + return this._tickWidth; + } + + public set tickWidth(tickWidth: number) { + if (tickWidth === this._tickWidth) { + return; + } + + this._tickWidth = tickWidth; + this.render(); + } + + public get tickColor(): string { + return this._color; + } + + public set tickColor(tickColor: string) { + if (tickColor === this._color) { + return; + } + + this._color = tickColor; + this.render(); + } + + public get showFullPixelValueRange(): boolean { + return this._showFullPixelValueRange; + } + + public set showFullPixelValueRange(showFullRange: boolean) { + if (showFullRange === this._showFullPixelValueRange) { + return; + } + + this._showFullPixelValueRange = showFullRange; + this.render(); + } + + public get visible() { + return this._canvas.style.display === 'block'; + } + + public set visible(visible) { + if (visible === this.visible) { + return; + } + + this._canvas.style.display = visible ? 'block' : 'none'; + + if (visible) { + this.render(); + } + } + + public appendTo(container: HTMLElement) { + container.appendChild(this._canvas); + this.render(); + } + + private static validateProps(props: ColorBarScaleProps) { + const { size, range, voiRange } = props; + + if (size && !isSizeValid(size)) { + throw new Error('Invalid "size"'); + } + + if (range && !isRangeValid(range)) { + throw new Error('Invalid "range"'); + } + + if (voiRange && !isRangeValid(voiRange)) { + throw new Error('Invalid "voiRange"'); + } + } + + private _setCanvasSize(canvas: HTMLCanvasElement, size: ColorBarSize) { + const { width, height } = size; + + canvas.width = width; + canvas.height = height; + + Object.assign(canvas.style, { + width: `${width}px`, + height: `${height}px`, + }); + } + + private _getCanvasPosition(canvas): ColorBarPosition { + const { top: canvasTop, left: canvasLeft } = this._canvas.style; + const top = Number.parseInt(canvasTop); + const left = Number.parseInt(canvasLeft); + + return { top, left }; + } + + private _setCanvasPosition( + canvas: HTMLCanvasElement, + position: ColorBarPosition + ) { + Object.assign(canvas.style, { + top: `${position.top}px`, + left: `${position.left}px`, + }); + } + + private _createCanvasElement( + size: ColorBarSize, + position: ColorBarPosition + ): HTMLCanvasElement { + const canvas = document.createElement('canvas'); + + Object.assign(canvas.style, { + display: 'none', + position: 'absolute', + boxSizing: 'border-box', + }); + + this._setCanvasSize(canvas, size); + this._setCanvasPosition(canvas, position); + + return canvas; + } + + /** + * Calculate "ticks" to be displayed for the current range + * @param range - Range with "lower" and "upper" values + */ + private _getTicks(range: ColorBarRange) { + const { lower, upper } = range; + const rangeValue = upper - lower; + + // First approximation + const roughStep = rangeValue / (this._maxNumTicks - 1); + + // Set best step for the range + const goodNormalizedSteps = [1, 2, 5, 10]; + + // Normalize rough step to find the normalized one that fits best + const stepPower = Math.pow( + 10, + -Math.floor(Math.log10(Math.abs(roughStep))) + ); + const normalizedStep = roughStep * stepPower; + const goodNormalizedStep = goodNormalizedSteps.find( + (n) => n >= normalizedStep + ); + const step = goodNormalizedStep / stepPower; + + // Determine the scale limits based on the chosen step. + const scaleMax = Math.ceil(upper / step) * step; + const scaleMin = Math.floor(lower / step) * step; + + const ticksCount = Math.round((scaleMax - scaleMin) / step) + 1; + const ticks = []; + + for (let i = 0; i < ticksCount; i++) { + ticks.push(scaleMin + i * step); + } + + return { scaleMin, scaleMax, step, ticks }; + } + + private _getLeftTickInfo({ position, labelMeasure }) { + const { width } = this._canvas; + const labelX = + width - this.tickSize - labelMeasure.width - this._labelMargin; + const labelPoint = [labelX, position]; + const tickPoints = { + start: [width - this._tickSize, position], + end: [width, position], + }; + + return { labelPoint, tickPoints }; + } + + private _getRightTickInfo({ position }) { + const labelPoint = [this._tickSize + this._labelMargin, position]; + const tickPoints = { + start: [0, position], + end: [this._tickSize, position], + }; + + return { labelPoint, tickPoints }; + } + + private _getTopTickInfo({ position, labelMeasure }) { + throw new Error('Not implemented'); + } + + private _getBottomTickInfo({ position, labelMeasure }) { + throw new Error('Not implemented'); + } + + private render() { + const { _canvas: canvas } = this; + + if (!canvas.isConnected || !this.visible) { + return; + } + + const { width, height } = canvas; + const isHorizontal = width >= height; + const maxCanvasPixelValue = isHorizontal ? width : height; + const canvasContext = canvas.getContext('2d'); + const { _voiRange: voiRange } = this; + const range = this._showFullPixelValueRange ? this._range : { ...voiRange }; + const rangeWidth = range.upper - range.lower; + const { ticks } = this._getTicks(range); + + canvasContext.clearRect(0, 0, width, height); + canvasContext.font = this._font; + canvasContext.textBaseline = 'middle'; + canvasContext.fillStyle = this._color; + canvasContext.strokeStyle = this._color; + canvasContext.lineWidth = this.tickWidth; + + ticks.forEach((tick) => { + let position = Math.round( + maxCanvasPixelValue * ((tick - range.lower) / rangeWidth) + ); + + // Zero at the bottom and max at the top of the colorBar on vertical colorBar + if (!isHorizontal) { + position = height - position; + } + + if (position < 0 || position > maxCanvasPixelValue) { + return; + } + + const label = tick.toString(); + const labelMeasure = canvasContext.measureText(label); + let tickInfo; + + if (isHorizontal) { + if (this._scalePosition === ColorBarScalePosition.TopOrLeft) { + tickInfo = this._getTopTickInfo({ position, labelMeasure }); + } else { + tickInfo = this._getBottomTickInfo({ position, labelMeasure }); + } + } else { + if (this._scalePosition === ColorBarScalePosition.TopOrLeft) { + tickInfo = this._getLeftTickInfo({ position, labelMeasure }); + } else { + tickInfo = this._getRightTickInfo({ position }); + } + } + + const { labelPoint, tickPoints } = tickInfo; + const { start: tickStart, end: tickEnd } = tickPoints; + + canvasContext.beginPath(); + canvasContext.moveTo(tickStart[0], tickStart[1]); + canvasContext.lineTo(tickEnd[0], tickEnd[1]); + canvasContext.fillText(label, labelPoint[0], labelPoint[1]); + canvasContext.stroke(); + + return position; + }); + } +} + +export { ColorBarScale as default, ColorBarScale as ColorBarVOIScale }; diff --git a/packages/core/src/ui/widgets/colorBar/EventListeners.ts b/packages/core/src/ui/widgets/colorBar/EventListeners.ts new file mode 100644 index 0000000000..f6591eb027 --- /dev/null +++ b/packages/core/src/ui/widgets/colorBar/EventListeners.ts @@ -0,0 +1,269 @@ +enum EventListenerPhases { + None = 0, + Capture = 1, + Bubble = 2, +} + +type ListenersMap = Map; + +class TargetEventListeners { + private _target: EventTarget; + private _eventListeners = new Map(); + private _children = new Map(); + + constructor(target: EventTarget) { + this._target = target; + } + + public get isEmpty() { + return this._eventListeners.size === 0 && this._children.size === 0; + } + + public addEventListener( + type: string, + callback: EventListener, + options?: AddEventListenerOptions + ) { + const dotIndex = type.indexOf('.'); + const isNamespace = dotIndex !== -1; + + if (isNamespace) { + const namespaceToken = type.substring(0, dotIndex); + let childElementEventListener = this._children.get(namespaceToken); + + if (!childElementEventListener) { + childElementEventListener = new TargetEventListeners(this._target); + this._children.set(namespaceToken, childElementEventListener); + } + + type = type.substring(dotIndex + 1); + childElementEventListener.addEventListener(type, callback, options); + } else { + this._addEventListener(type, callback, options); + } + } + + /** + * Remove an event listener with support for namespaces and optional callback + * which makes it remove all listeners of a given type + * @param type - Event type + * @param callback - Event listener + * @param options - Event options + */ + public removeEventListener( + type: string, + callback?: EventListener, + options?: EventListenerOptions + ): void { + const dotIndex = type.indexOf('.'); + const isNamespace = dotIndex !== -1; + + if (isNamespace) { + const namespaceToken = type.substring(0, dotIndex); + const childElementEventListener = this._children.get(namespaceToken); + + if (!childElementEventListener) { + return; + } + + type = type.substring(dotIndex + 1); + childElementEventListener.removeEventListener(type, callback, options); + + // remove empty child objects + if (childElementEventListener.isEmpty) { + this._children.delete(namespaceToken); + } + } else { + this._removeEventListener(type, callback, options); + } + } + + /** + * Loop through all types, listeners and phases removing all of them + */ + public dispose() { + // Dispose all children (DFS - depth first search) + Array.from(this._children.entries()).forEach(([namespace, child]) => { + child.dispose(); + + if (child.isEmpty) { + this._children.delete(namespace); + } else { + // This scenario must never happen (safety only) + throw new Error('Child is not empty and cannot be removed'); + } + }); + + this._unregisterAllEvents(); + } + + private _addEventListener( + type: string, + callback: EventListener, + options?: AddEventListenerOptions + ) { + let listenersMap = this._eventListeners.get(type); + + if (!listenersMap) { + listenersMap = new Map(); + this._eventListeners.set(type, listenersMap); + } + + const useCapture = options?.capture ?? false; + const listenerPhase = useCapture + ? EventListenerPhases.Capture + : EventListenerPhases.Bubble; + const registeredPhases = + listenersMap.get(callback) ?? EventListenerPhases.None; + + // Bitwise operator to see if the current phase is already registered + // because the same listener may be register twice (capturing and bubbling + // phases) + if (registeredPhases & listenerPhase) { + console.warn('A listener is already registered for this phase'); + return; + } + + // Add a new event listener or updates the existing one for the phase requested + listenersMap.set(callback, registeredPhases | listenerPhase); + + // Add the event listener to the target + this._target.addEventListener(type, callback, options); + } + + private _removeEventListener( + type: string, + callback?: EventListener, + options?: EventListenerOptions + ): void { + const useCapture = options?.capture ?? false; + const listenerPhase = useCapture + ? EventListenerPhases.Capture + : EventListenerPhases.Bubble; + + const listenersMap = this._eventListeners.get(type); + + if (!listenersMap) { + return; + } + + // It can remove a single or all callbacks for a given namespace + const callbacks = callback ? [callback] : Array.from(listenersMap.keys()); + + callbacks.forEach((callbackItem) => { + const registeredPhases = + listenersMap.get(callbackItem) ?? EventListenerPhases.None; + + // Bitwise operation to see if the phase is registered + const phaseRegistered = !!(registeredPhases & listenerPhase); + + if (!phaseRegistered) { + return; + } + + // Remove the event listener from the target + this._target.removeEventListener(type, callbackItem, options); + + // Since it is enabled we can XOR it to zero the bit in that position + // 00000011 (capture & buble) ^ 00000010 (buble) = 00000001 (capture) + const newListenerPhase = registeredPhases ^ listenerPhase; + + // Deletes the listener if it is no more used in capturing or bubbling + // phases or updates it otherwise + if (newListenerPhase === EventListenerPhases.None) { + listenersMap.delete(callbackItem); + } else { + listenersMap.set(callbackItem, newListenerPhase); + } + }); + + // Deletes the event from the main map if there are no listeners anymore + if (!listenersMap.size) { + this._eventListeners.delete(type); + } + } + + private _unregisterAllListeners(type: string, listenersMap: ListenersMap) { + // Creates a copy with Array.from() because the map mutates every + // time an event listener is removed + Array.from(listenersMap.entries()).forEach(([listener, eventPhases]) => { + const startPhase = EventListenerPhases.Capture; + + // currentPhase start at 1 and shifts 1 bit to the left because + // EventListenerPhases is a power of 2 + for (let currentPhase = startPhase; eventPhases; currentPhase <<= 1) { + // Check if the current phase is registered + if (!(eventPhases & currentPhase)) { + continue; + } + + const useCapture = + currentPhase === EventListenerPhases.Capture ? true : false; + + // Remove the event listener for this given phase + this.removeEventListener(type, listener, { capture: useCapture }); + + // Switch the bit from the "currentPhase" from 1 to 0 + eventPhases ^= currentPhase; + } + }); + } + + private _unregisterAllEvents() { + // Creates a copy with Array.from() because the map mutates every + // time an event listener is removed + Array.from(this._eventListeners.entries()).forEach(([type, listenersMap]) => + this._unregisterAllListeners(type, listenersMap) + ); + } +} + +class EventListenersManager { + private _targetsEventListeners = new Map(); + + public addEventListener( + target: EventTarget, + type: string, + callback: EventListener, + options?: AddEventListenerOptions + ) { + let eventListeners = this._targetsEventListeners.get(target); + + if (!eventListeners) { + eventListeners = new TargetEventListeners(target); + this._targetsEventListeners.set(target, eventListeners); + } + + eventListeners.addEventListener(type, callback, options); + } + + public removeEventListener( + target: EventTarget, + type: string, + callback?: EventListener, + options?: EventListenerOptions + ) { + const eventListeners = this._targetsEventListeners.get(target); + + if (!eventListeners) { + return; + } + + eventListeners.removeEventListener(type, callback, options); + + if (eventListeners.isEmpty) { + this._targetsEventListeners.delete(target); + } + } + + public dispose() { + Array.from(this._targetsEventListeners.entries()).forEach( + ([target, targetEventListeners]) => { + targetEventListeners.dispose(); + this._targetsEventListeners.delete(target); + } + ); + } +} + +export { TargetEventListeners, EventListenersManager }; diff --git a/packages/core/src/ui/widgets/colorBar/ViewportColorBar.ts b/packages/core/src/ui/widgets/colorBar/ViewportColorBar.ts new file mode 100644 index 0000000000..2cee203b09 --- /dev/null +++ b/packages/core/src/ui/widgets/colorBar/ViewportColorBar.ts @@ -0,0 +1,173 @@ +import { + eventTarget, + VolumeViewport, + StackViewport, + Types, + Enums, + utilities, + getEnabledElement, + cache, +} from '@cornerstonejs/core'; +import { ColorBar } from './ColorBar'; +import { ColorBarProps, ColorBarVOIRange } from './types'; + +const { Events } = Enums; +const DEFAULT_MULTIPLIER = 4; + +export interface ViewportColorBarProps extends ColorBarProps { + element: HTMLDivElement; + volumeId?: string; +} + +class ViewportColorBar extends ColorBar { + private _element: HTMLDivElement; + private _volumeId: string; + + constructor(props: ViewportColorBarProps) { + super({ + ...props, + range: ViewportColorBar._getRange(props.element, props.volumeId), + voiRange: ViewportColorBar._getVOIRange(props.element, props.volumeId), + }); + + this._element = props.element; + this._volumeId = props.volumeId; + + this._addCornerstoneEventListener(); + } + + public get element() { + return this._element; + } + + public get enabledElement() { + return getEnabledElement(this._element); + } + + protected getVOIMultipliers(): [number, number] { + const { viewport } = this.enabledElement; + // const {actor: volumeActor } = viewport.getActor(this._volumeId) + const volume = cache.getVolume(this._volumeId); + const { scaling } = volume; + const isPreScaled = !!scaling && Object.keys(scaling).length > 0; + const { Modality: modality } = volume.metadata; + + if (modality === 'PT') { + const ptMultiplier = + 5 / Math.max(this.containerSize.width, this.containerSize.height); + + return isPreScaled ? [0, ptMultiplier] : [0, DEFAULT_MULTIPLIER]; + } + + return [DEFAULT_MULTIPLIER, DEFAULT_MULTIPLIER]; + } + + protected voiChanged(voiRange: ColorBarVOIRange) { + super.voiChanged(voiRange); + + const { viewport } = this.enabledElement; + + if (viewport instanceof StackViewport) { + viewport.setProperties({ + voiRange: voiRange, + }); + viewport.render(); + } else if (viewport instanceof VolumeViewport) { + const { _volumeId: volumeId } = this; + const viewportsContainingVolumeUID = utilities.getViewportsWithVolumeId( + volumeId, + viewport.renderingEngineId + ); + + viewport.setProperties({ voiRange }, volumeId); + viewportsContainingVolumeUID.forEach((vp) => vp.render()); + } + } + + private static _getRange(element, volumeId) { + const defaultValue = { lower: -1000, upper: 1000 }; + const enabledElement = getEnabledElement(element); + const { viewport } = enabledElement; + + const actor = volumeId + ? viewport.getActor(volumeId) + : viewport.getDefaultActor(); + + if (!actor) { + return defaultValue; + } + + const imageData = actor.actor.getMapper().getInputData(); + const range = imageData.getPointData().getScalars().getRange(); + + return range[0] === 0 && range[1] === 0 + ? defaultValue + : { lower: range[0], upper: range[1] }; + } + + private static _getVOIRange(element, volumeId) { + const defaultValue = { lower: -1000, upper: 1000 }; + const enabledElement = getEnabledElement(element); + const { viewport } = enabledElement; + + const actor = volumeId + ? viewport.getActor(volumeId) + : viewport.getDefaultActor(); + + if (!actor) { + return { lower: -1000, upper: 1000 }; + } + + const voiRange = actor.actor + .getProperty() + .getRGBTransferFunction(0) + .getRange(); + + return voiRange[0] === 0 && voiRange[1] === 0 + ? defaultValue + : { lower: voiRange[0], upper: voiRange[1] }; + } + + private _imageVolumeModifiedCallback = ( + evt: Types.EventTypes.ImageVolumeModifiedEvent + ) => { + const { volumeId } = evt.detail.imageVolume; + + if (volumeId !== this._volumeId) { + return; + } + + const { _element: element } = this; + + this.range = ViewportColorBar._getRange(element, volumeId); + }; + + private _viewportVOIModifiedCallback = ( + evt: Types.EventTypes.VoiModifiedEvent + ) => { + const { viewportId, volumeId, range } = evt.detail; + const { viewport } = this.enabledElement; + + if (viewportId != viewport.id || volumeId !== this._volumeId) { + return; + } + + this.voiRange = range; + }; + + private _addCornerstoneEventListener() { + const { _element: element } = this; + + eventTarget.addEventListener( + Events.IMAGE_VOLUME_MODIFIED, + this._imageVolumeModifiedCallback + ); + + element.addEventListener( + Events.VOI_MODIFIED, + this._viewportVOIModifiedCallback + ); + } +} + +export { ViewportColorBar as default, ViewportColorBar as ViewportColorBar }; diff --git a/packages/core/src/ui/widgets/colorBar/common/isRangeValid.ts b/packages/core/src/ui/widgets/colorBar/common/isRangeValid.ts new file mode 100644 index 0000000000..65895444a4 --- /dev/null +++ b/packages/core/src/ui/widgets/colorBar/common/isRangeValid.ts @@ -0,0 +1,7 @@ +import { ColorBarRange } from '../types/ColorBarRange'; + +const isRangeValid = (range: ColorBarRange) => { + return range && range.upper > range.lower; +}; + +export { isRangeValid as default, isRangeValid }; diff --git a/packages/core/src/ui/widgets/colorBar/common/isSizeValid.ts b/packages/core/src/ui/widgets/colorBar/common/isSizeValid.ts new file mode 100644 index 0000000000..23b5a97e7a --- /dev/null +++ b/packages/core/src/ui/widgets/colorBar/common/isSizeValid.ts @@ -0,0 +1,7 @@ +import { ColorBarSize } from '../types/ColorBarSize'; + +const isSizeValid = (size: ColorBarSize) => { + return !!size && size.width > 0 && size.height > 0; +}; + +export { isSizeValid as default, isSizeValid }; diff --git a/packages/core/src/ui/widgets/colorBar/common/positionsEquals.ts b/packages/core/src/ui/widgets/colorBar/common/positionsEquals.ts new file mode 100644 index 0000000000..3b0d0f79c8 --- /dev/null +++ b/packages/core/src/ui/widgets/colorBar/common/positionsEquals.ts @@ -0,0 +1,7 @@ +import { ColorBarPosition } from '../types/ColorBarPosition'; + +const positionsEqual = (a: ColorBarPosition, b: ColorBarPosition) => { + return !!a && !!b && a.left === b.left && a.top === b.top; +}; + +export { positionsEqual as default, positionsEqual }; diff --git a/packages/core/src/ui/widgets/colorBar/common/rangesEqual.ts b/packages/core/src/ui/widgets/colorBar/common/rangesEqual.ts new file mode 100644 index 0000000000..91e87500d1 --- /dev/null +++ b/packages/core/src/ui/widgets/colorBar/common/rangesEqual.ts @@ -0,0 +1,7 @@ +import { ColorBarRange } from '../types/ColorBarRange'; + +const rangesEqual = (a: ColorBarRange, b: ColorBarRange) => { + return !!a && !!b && a.lower === b.lower && a.upper === b.upper; +}; + +export { rangesEqual as default, rangesEqual }; diff --git a/packages/core/src/ui/widgets/colorBar/common/sizesEqual.ts b/packages/core/src/ui/widgets/colorBar/common/sizesEqual.ts new file mode 100644 index 0000000000..f4cd303e78 --- /dev/null +++ b/packages/core/src/ui/widgets/colorBar/common/sizesEqual.ts @@ -0,0 +1,7 @@ +import { ColorBarSize } from '../types/ColorBarSize'; + +const sizesEqual = (a: ColorBarSize, b: ColorBarSize) => { + return !!a && !!b && a.width === b.width && a.height === b.height; +}; + +export { sizesEqual as default, sizesEqual }; diff --git a/packages/core/src/ui/widgets/colorBar/enums/ColorBarScalePosition.ts b/packages/core/src/ui/widgets/colorBar/enums/ColorBarScalePosition.ts new file mode 100644 index 0000000000..2b1c3a9593 --- /dev/null +++ b/packages/core/src/ui/widgets/colorBar/enums/ColorBarScalePosition.ts @@ -0,0 +1,4 @@ +export enum ColorBarScalePosition { + TopOrLeft, + BottomOrRight, +} diff --git a/packages/core/src/ui/widgets/colorBar/enums/index.ts b/packages/core/src/ui/widgets/colorBar/enums/index.ts new file mode 100644 index 0000000000..efd1f14d3d --- /dev/null +++ b/packages/core/src/ui/widgets/colorBar/enums/index.ts @@ -0,0 +1 @@ +export { ColorBarScalePosition } from './ColorBarScalePosition'; diff --git a/packages/core/src/ui/widgets/colorBar/index.ts b/packages/core/src/ui/widgets/colorBar/index.ts new file mode 100644 index 0000000000..aff71cd30a --- /dev/null +++ b/packages/core/src/ui/widgets/colorBar/index.ts @@ -0,0 +1,5 @@ +export * as Enums from './enums'; +export type * as Types from './types'; + +export { ColorBar } from './ColorBar'; +export { ViewportColorBar } from './ViewportColorBar'; diff --git a/packages/core/src/ui/widgets/colorBar/types/ColorBarCanvasProps.ts b/packages/core/src/ui/widgets/colorBar/types/ColorBarCanvasProps.ts new file mode 100644 index 0000000000..2b78175be0 --- /dev/null +++ b/packages/core/src/ui/widgets/colorBar/types/ColorBarCanvasProps.ts @@ -0,0 +1,14 @@ +import { ColorBarRange } from './ColorBarRange'; +import { ColorBarSize } from './ColorBarSize'; +import { ColorBarVOIRange } from './ColorBarVOIRange'; +import { Colormap } from './Colormap'; + +export interface ColorBarCanvasProps { + colormap: Colormap; + size?: ColorBarSize; + range?: ColorBarRange; + voiRange?: ColorBarVOIRange; + + container?: HTMLElement; + showFullPixelValueRange?: boolean; +} diff --git a/packages/core/src/ui/widgets/colorBar/types/ColorBarPosition.ts b/packages/core/src/ui/widgets/colorBar/types/ColorBarPosition.ts new file mode 100644 index 0000000000..bcb7cedd88 --- /dev/null +++ b/packages/core/src/ui/widgets/colorBar/types/ColorBarPosition.ts @@ -0,0 +1,4 @@ +export type ColorBarPosition = { + top: number; + left: number; +}; diff --git a/packages/core/src/ui/widgets/colorBar/types/ColorBarProps.ts b/packages/core/src/ui/widgets/colorBar/types/ColorBarProps.ts new file mode 100644 index 0000000000..22ea9abb3f --- /dev/null +++ b/packages/core/src/ui/widgets/colorBar/types/ColorBarProps.ts @@ -0,0 +1,16 @@ +import { WidgetProps } from '../../Widget'; +import { ColorBarScalePosition } from '../enums/ColorBarScalePosition'; +import { ColorBarRange } from './ColorBarRange'; +import { ColorBarScaleStyle } from './ColorBarScaleStyle'; +import { ColorBarVOIRange } from './ColorBarVOIRange'; +import { Colormap } from './Colormap'; + +export interface ColorBarProps extends WidgetProps { + colormaps: Colormap[]; + activeColormapName?: string; + range?: ColorBarRange; + voiRange?: ColorBarVOIRange; + scalePosition?: ColorBarScalePosition; + scaleStyle?: ColorBarScaleStyle; + showFullPixelValueRange?: boolean; +} diff --git a/packages/core/src/ui/widgets/colorBar/types/ColorBarRange.ts b/packages/core/src/ui/widgets/colorBar/types/ColorBarRange.ts new file mode 100644 index 0000000000..f32760b901 --- /dev/null +++ b/packages/core/src/ui/widgets/colorBar/types/ColorBarRange.ts @@ -0,0 +1,4 @@ +export type ColorBarRange = { + lower: number; + upper: number; +}; diff --git a/packages/core/src/ui/widgets/colorBar/types/ColorBarScaleProps.ts b/packages/core/src/ui/widgets/colorBar/types/ColorBarScaleProps.ts new file mode 100644 index 0000000000..9356a719ab --- /dev/null +++ b/packages/core/src/ui/widgets/colorBar/types/ColorBarScaleProps.ts @@ -0,0 +1,17 @@ +import { ColorBarScalePosition } from '../enums/ColorBarScalePosition'; +import { ColorBarPosition } from './ColorBarPosition'; +import { ColorBarRange } from './ColorBarRange'; +import { ColorBarScaleStyle } from './ColorBarScaleStyle'; +import { ColorBarSize } from './ColorBarSize'; +import { ColorBarVOIRange } from './ColorBarVOIRange'; + +export interface ColorBarScaleProps { + size?: ColorBarSize; + position?: ColorBarPosition; + range: ColorBarRange; + voiRange: ColorBarVOIRange; + scaleStyle?: ColorBarScaleStyle; + scalePosition?: ColorBarScalePosition; + container?: HTMLElement; + showFullPixelValueRange?: boolean; +} diff --git a/packages/core/src/ui/widgets/colorBar/types/ColorBarScaleStyle.ts b/packages/core/src/ui/widgets/colorBar/types/ColorBarScaleStyle.ts new file mode 100644 index 0000000000..93441daa0c --- /dev/null +++ b/packages/core/src/ui/widgets/colorBar/types/ColorBarScaleStyle.ts @@ -0,0 +1,8 @@ +export interface ColorBarScaleStyle { + font?: string; + color?: string; + tickSize?: number; + tickWidth?: number; + labelMargin?: number; + maxNumTicks?: number; +} diff --git a/packages/core/src/ui/widgets/colorBar/types/ColorBarSize.ts b/packages/core/src/ui/widgets/colorBar/types/ColorBarSize.ts new file mode 100644 index 0000000000..95d6dc4e02 --- /dev/null +++ b/packages/core/src/ui/widgets/colorBar/types/ColorBarSize.ts @@ -0,0 +1,4 @@ +export type ColorBarSize = { + width: number; + height: number; +}; diff --git a/packages/core/src/ui/widgets/colorBar/types/ColorBarVOIRange.ts b/packages/core/src/ui/widgets/colorBar/types/ColorBarVOIRange.ts new file mode 100644 index 0000000000..7435869d61 --- /dev/null +++ b/packages/core/src/ui/widgets/colorBar/types/ColorBarVOIRange.ts @@ -0,0 +1,3 @@ +import type { ColorBarRange } from './ColorBarRange'; + +export type ColorBarVOIRange = ColorBarRange; diff --git a/packages/core/src/ui/widgets/colorBar/types/Colormap.ts b/packages/core/src/ui/widgets/colorBar/types/Colormap.ts new file mode 100644 index 0000000000..734279a52f --- /dev/null +++ b/packages/core/src/ui/widgets/colorBar/types/Colormap.ts @@ -0,0 +1,5 @@ +export type Colormap = { + ColorSpace: string; + Name: string; + RGBPoints: number[]; +}; diff --git a/packages/core/src/ui/widgets/colorBar/types/ColormapCanvasProps.ts b/packages/core/src/ui/widgets/colorBar/types/ColormapCanvasProps.ts new file mode 100644 index 0000000000..2b78175be0 --- /dev/null +++ b/packages/core/src/ui/widgets/colorBar/types/ColormapCanvasProps.ts @@ -0,0 +1,14 @@ +import { ColorBarRange } from './ColorBarRange'; +import { ColorBarSize } from './ColorBarSize'; +import { ColorBarVOIRange } from './ColorBarVOIRange'; +import { Colormap } from './Colormap'; + +export interface ColorBarCanvasProps { + colormap: Colormap; + size?: ColorBarSize; + range?: ColorBarRange; + voiRange?: ColorBarVOIRange; + + container?: HTMLElement; + showFullPixelValueRange?: boolean; +} diff --git a/packages/core/src/ui/widgets/colorBar/types/index.ts b/packages/core/src/ui/widgets/colorBar/types/index.ts new file mode 100644 index 0000000000..40ec91d559 --- /dev/null +++ b/packages/core/src/ui/widgets/colorBar/types/index.ts @@ -0,0 +1,4 @@ +export { ColorBarProps } from './ColorBarProps'; +export { ColorBarRange } from './ColorBarRange'; +export { ColorBarVOIRange } from './ColorBarVOIRange'; +export { Colormap } from './Colormap'; diff --git a/packages/core/src/ui/widgets/index.ts b/packages/core/src/ui/widgets/index.ts new file mode 100644 index 0000000000..9ab9b6c816 --- /dev/null +++ b/packages/core/src/ui/widgets/index.ts @@ -0,0 +1 @@ +export * as colorbar from './colorBar'; diff --git a/utils/demo/helpers/addDropdownToToolbar.ts b/utils/demo/helpers/addDropdownToToolbar.ts index 2e2b2b9c93..b4d30da26d 100644 --- a/utils/demo/helpers/addDropdownToToolbar.ts +++ b/utils/demo/helpers/addDropdownToToolbar.ts @@ -2,11 +2,13 @@ export default function addDropDownToToolbar({ id, options, container, + style, onSelectedValueChange, }: { id?: string; options: { values: number[] | string[]; defaultValue: number | string }; container?: HTMLElement; + style?: Record; onSelectedValueChange: (value: number | string) => void; }) { const { values, defaultValue } = options; @@ -14,6 +16,10 @@ export default function addDropDownToToolbar({ select.id = id; + if (style) { + Object.assign(select.style, style); + } + values.forEach((value) => { const optionElement = document.createElement('option'); From 7017e50844f82cc8bbfc6aca838a4875fd9d098c Mon Sep 17 00:00:00 2001 From: Leonardo Campos Date: Fri, 20 Oct 2023 12:33:32 -0300 Subject: [PATCH 02/14] restore point --- packages/core/examples/colorbar/index.ts | 309 ------------------ .../src/ui/widgets/colorBar/ColorBarCanvas.ts | 2 +- .../src/ui/widgets/colorBar/ColormapList.ts | 5 + .../ui/widgets/colorBar/ColormapListItem.ts | 11 + 4 files changed, 17 insertions(+), 310 deletions(-) create mode 100644 packages/core/src/ui/widgets/colorBar/ColormapList.ts create mode 100644 packages/core/src/ui/widgets/colorBar/ColormapListItem.ts diff --git a/packages/core/examples/colorbar/index.ts b/packages/core/examples/colorbar/index.ts index 6b32a9d2ab..46cf60e865 100644 --- a/packages/core/examples/colorbar/index.ts +++ b/packages/core/examples/colorbar/index.ts @@ -36,9 +36,6 @@ const { const { MouseBindings } = csToolsEnums; -const pause = (interval) => - new Promise((resolve) => setTimeout(resolve, interval)); - const { ViewportType } = Enums; const renderingEngineId = 'myRenderingEngine'; const stackViewportId = 'CT_STACK'; @@ -60,8 +57,6 @@ const colormaps = vtkColormaps.rgbPresetNames.map( let currentPTColormapName = 'Black-Body Radiation'; let ctColorBar = null; let ptColorBar = null; -const voiRangeMin = 0; -const voiRangeMax = 1; // ==[ Set up page ]============================================================ @@ -110,32 +105,6 @@ addInstruction('- Click and drag at the color bar to change VOI'); const colorBarSize = { shortSide: 20, longSide: 250 }; -// const ctColorBar = new ViewportColorBar({ -// id: 'ctColorBar', -// viewportId: volumeViewportId, -// renderingEngineId, -// colormaps, -// activeColormapName: 'Grayscale', -// // voiRange: { min: 0.25, max: 0.75 }, -// }); - -// const ptColorBar = new ViewportColorBar({ -// id: 'ptColorBar', -// viewportId: volumeViewportId, -// renderingEngineId, -// colormaps, -// activeColormapName: currentPTColormapName, -// // voiRange: { min: 0.25, max: 0.75 }, -// }); - -// const colorBars = [ctColorBar, ptColorBar */]; - -// ctColorBar.rootElement.draggable = true; -// ctColorBar.rootElement.style.cursor = 'move'; - -// ptColorBar.rootElement.draggable = true; -// ptColorBar.rootElement.style.cursor = 'move'; - Object.assign(bottomLeftContainer.style, { position: 'absolute', top: '100%', @@ -190,158 +159,11 @@ containers.forEach((container) => { cursor: 'initial', }); - // container.addEventListener('dragover', (evt) => evt.preventDefault()); - // container.addEventListener('drop', (evt: DragEvent) => { - // const target = evt.target as HTMLElement; - // const rawTransferedData = evt.dataTransfer.getData('application/json'); - // const transferedData = JSON.parse(rawTransferedData); - // const colorBar = colorBars.find( - // (colorBar) => colorBar.id === transferedData.colorBarId - // ); - // const sourceContainer = colorBar.rootElement.parentElement; - // const containersSet = new Set(containers); - // let targetContainer = null; - - // // If the element is dropped into the same container the `target` will be the - // // canvas element and we need to search for some parent element that is a container - - // for (let node = target; node !== null; node = node.parentElement) { - // if (containersSet.has(node)) { - // targetContainer = node; - // break; - // } - // } - - // if (!targetContainer) { - // return; - // } - - // const swapColorBars = targetContainer.hasChildNodes(); - - // colorBar.appendTo(targetContainer); - - // if (swapColorBars) { - // const otherColorBar = colorBars.find( - // (colorBar) => colorBar.id !== transferedData.colorBarId - // ); - // otherColorBar.appendTo(sourceContainer); - // } - - // evt.preventDefault(); - // }); - containersMutationObserver.observe(container, { childList: true }); }); -// colorBars.forEach((colorBar) => { -// colorBar.rootElement.addEventListener('dragstart', (evt) => { -// evt.dataTransfer.effectAllowed = 'move'; -// evt.dataTransfer.setData( -// 'application/json', -// JSON.stringify({ -// colorBarId: colorBar.id, -// }) -// ); - -// containers.forEach((container) => -// Object.assign(container.style, { -// display: 'block', -// backgroundColor: 'rgba(0, 255, 0, 0.2)', -// }) -// ); -// }); - -// colorBar.rootElement.addEventListener('dragend', () => { -// containers.forEach( -// (container) => (container.style.backgroundColor = 'none') -// ); - -// containers.forEach((container) => -// Object.assign(container.style, { -// display: container.hasChildNodes() ? 'block' : 'none', -// backgroundColor: 'none', -// }) -// ); -// }); -// }); - -const runTestsButton = document.createElement('button'); - -// runTestsButton.style.marginTop = '20px'; -// runTestsButton.textContent = 'Run dev tests'; -// runTestsButton.addEventListener('click', () => runTests()); -// content.appendChild(runTestsButton); - // ==[ Toolbar ]================================================================ -// addButtonToToolbar({ -// title: 'Set CT VOI Range', -// onClick: () => { -// // Get the rendering engine -// const renderingEngine = getRenderingEngine(renderingEngineId); - -// // Get the stack viewport -// const viewport = ( -// renderingEngine.getViewport(volumeViewportId) -// ); - -// viewport.setProperties({ voiRange: { lower: -1500, upper: 2500 } }); -// viewport.render(); -// }, -// }); - -// addButtonToToolbar({ -// title: 'Reset Viewport', -// onClick: () => { -// // Get the rendering engine -// const renderingEngine = getRenderingEngine(renderingEngineId); - -// // Get the volume viewport -// const viewport = ( -// renderingEngine.getViewport(volumeViewportId) -// ); - -// // Resets the viewport's camera -// viewport.resetCamera(); -// // TODO reset the viewport properties, we don't have API for this. - -// viewport.render(); -// }, -// }); - -// const fused = false; - -// addButtonToToolbar({ -// title: 'toggle PET', -// onClick: async () => { -// // Get the rendering engine -// const renderingEngine = getRenderingEngine(renderingEngineId); -// // Get the volume viewport -// const viewport = ( -// renderingEngine.getViewport(volumeViewportId) -// ); -// if (fused) { -// // Removes the PT actor from the scene -// viewport.removeVolumeActors([ptVolumeId], true); -// fused = false; -// } else { -// // Add the PET volume to the viewport. It is in the same DICOM Frame Of Reference/worldspace -// // If it was in a different frame of reference, you would need to register it first. -// await viewport.addVolumes( -// [ -// { -// volumeId: ptVolumeId, -// callback: setPetColorMapTransferFunctionForVolumeActor, -// }, -// ], -// true -// ); -// setPTColormap(currentPTColormapName); -// fused = true; -// } -// }, -// }); - const orientationOptions = { axial: 'axial', sagittal: 'sagittal', @@ -408,137 +230,6 @@ addDropdownToToolbar({ }, }); -// addSliderToToolbar({ -// title: 'VOI min', -// range: [0, 1], -// step: 0.05, -// defaultValue: voiRangeMin, -// onSelectedValueChange: (value) => { -// voiRangeMin = parseFloat(value); -// // ptColorBar.voiRange = { min: voiRangeMin, max: voiRangeMax }; -// }, -// }); - -// addSliderToToolbar({ -// title: 'VOI max', -// range: [0, 1], -// step: 0.05, -// defaultValue: voiRangeMax, -// onSelectedValueChange: (value) => { -// voiRangeMax = parseFloat(value); -// // ptColorBar.voiRange = { min: voiRangeMin, max: voiRangeMax }; -// }, -// }); - -// ==[ Dev Tests ]============================================================== - -// async function testVoiRange() { -// console.log('Testing VOI range'); - -// const numLoops = 4; -// const numMoves = 60; -// const pauseTime = 1000 / 60; // (1000 / fps) -// const windowWidth = 0.5; - -// for (let numLoop = 0; numLoop < numLoops; numLoop++) { -// const leftToRight = !(numLoop % 2); -// const iStart = numLoop ? 1 : Math.floor(numMoves / 2); -// const iEnd = numLoop === numLoops - 1 ? Math.floor(numMoves / 2) : numMoves; - -// for (let i = iStart; i < iEnd; i++) { -// const position = leftToRight ? i : numMoves - i - 1; -// const windowCenter = position * (1 / (numMoves - 1)); -// const min = windowCenter - windowWidth / 2; -// const max = min + windowWidth; - -// ptColorBar.voiRange = { min, max }; - -// await pause(pauseTime); -// } -// } - -// // Restore voiRange to max -// ptColorBar.voiRange = { min: 0, max: 1 }; -// } - -// // Tests to make sure it works when resizing the container or attaching to a new container -// async function testContainers() { -// await pause(500); - -// console.log('append to right containers'); -// ctColorBar.appendTo(rightTopContainer); -// ptColorBar.appendTo(rightBottomContainer); - -// await pause(500); - -// console.log('update right containers size'); -// Object.assign(rightTopContainer.style, { -// height: `${colorBarSize.longSide - 100}px`, -// }); -// Object.assign(rightBottomContainer.style, { -// height: `${colorBarSize.longSide - 100}px`, -// }); - -// await pause(500); - -// console.log('append to bottom containers'); -// ctColorBar.appendTo(bottomLeftContainer); -// ptColorBar.appendTo(bottomRightContainer); - -// // Restore right containers size -// Object.assign(rightTopContainer.style, { -// height: `${colorBarSize.longSide}px`, -// }); -// Object.assign(rightBottomContainer.style, { -// height: `${colorBarSize.longSide}px`, -// }); - -// await pause(500); - -// console.log('update bottom containers size'); -// Object.assign(bottomLeftContainer.style, { -// width: `${colorBarSize.longSide - 100}px`, -// }); -// Object.assign(bottomRightContainer.style, { -// width: `${colorBarSize.longSide - 100}px`, -// }); - -// await pause(500); - -// // Restore bottom containers size -// Object.assign(bottomLeftContainer.style, { -// width: `${colorBarSize.longSide}px`, -// }); -// Object.assign(bottomRightContainer.style, { -// width: `${colorBarSize.longSide}px`, -// }); -// } - -// async function testPTColormaps() { -// for (let i = 1, len = colormaps.length; i < len; i++) { -// console.log(`Colormap: ${colormaps[i].Name}`); -// ptColorBar.activeColormapName = colormaps[i].Name; -// await pause(100); -// } - -// // Back to the first colormap -// console.log(`Colormap: ${colormaps[0].Name}`); -// ptColorBar.activeColormapName = colormaps[0].Name; -// } - -async function runTests() { - // const currentCTParent = ctColorBar.rootElement.parentElement; - // const currentPTParent = ptColorBar.rootElement.parentElement; - // console.log('Dev tests started'); - // await testContainers(); - // // Add them back to theirs parents - // ctColorBar.appendTo(currentCTParent); - // ptColorBar.appendTo(currentPTParent); - // await testPTColormaps(); - // await testVoiRange(); - // console.log('Dev tests complete'); -} - // ============================================================================= function setPTColormap(colormapName: string) { diff --git a/packages/core/src/ui/widgets/colorBar/ColorBarCanvas.ts b/packages/core/src/ui/widgets/colorBar/ColorBarCanvas.ts index e2d0bda887..b188af583e 100644 --- a/packages/core/src/ui/widgets/colorBar/ColorBarCanvas.ts +++ b/packages/core/src/ui/widgets/colorBar/ColorBarCanvas.ts @@ -1,6 +1,6 @@ import { utilities } from '@cornerstonejs/core'; import { ColorBarRange, ColorBarVOIRange, Colormap } from './types'; -import { ColorBarCanvasProps } from './types/ColormapCanvasProps'; +import { ColorBarCanvasProps } from './types/ColorBarCanvasProps'; import { ColorBarSize } from './types/ColorBarSize'; import isRangeValid from './common/isRangeValid'; import rangesEqual from './common/rangesEqual'; diff --git a/packages/core/src/ui/widgets/colorBar/ColormapList.ts b/packages/core/src/ui/widgets/colorBar/ColormapList.ts new file mode 100644 index 0000000000..1fee41fecd --- /dev/null +++ b/packages/core/src/ui/widgets/colorBar/ColormapList.ts @@ -0,0 +1,5 @@ +class ColormapList { + // +} + +export { ColormapList as default, ColormapList }; diff --git a/packages/core/src/ui/widgets/colorBar/ColormapListItem.ts b/packages/core/src/ui/widgets/colorBar/ColormapListItem.ts new file mode 100644 index 0000000000..973b344bdd --- /dev/null +++ b/packages/core/src/ui/widgets/colorBar/ColormapListItem.ts @@ -0,0 +1,11 @@ +import ColormapCanvas from './ColorBarCanvas'; + +class ColormapListItem { + private _canvas: ColormapCanvas; + + constructor() { + this._canvas = new ColormapCanvas(); + } +} + +export { ColormapListItem as default, ColormapListItem }; From 805fefe0f656a4f66445b03ff4d01a838619a529 Mon Sep 17 00:00:00 2001 From: Leonardo Campos Date: Mon, 23 Oct 2023 15:57:06 -0300 Subject: [PATCH 03/14] changed color bar example page --- packages/core/examples/colorbar/index.ts | 655 +++++++++++------- .../core/src/ui/widgets/colorBar/ColorBar.ts | 1 + .../ui/widgets/colorBar/ViewportColorBar.ts | 43 +- 3 files changed, 424 insertions(+), 275 deletions(-) diff --git a/packages/core/examples/colorbar/index.ts b/packages/core/examples/colorbar/index.ts index 46cf60e865..cd3031e3dd 100644 --- a/packages/core/examples/colorbar/index.ts +++ b/packages/core/examples/colorbar/index.ts @@ -3,19 +3,20 @@ import { RenderingEngine, Types, Enums, + cache, volumeLoader, getRenderingEngine, ui, } from '@cornerstonejs/core'; -import * as cornerstoneTools from '@cornerstonejs/tools'; import { initDemo, createImageIdsAndCacheMetaData, - setTitleAndDescription, - addDropdownToToolbar, setCtTransferFunctionForVolumeActor, setPetColorMapTransferFunctionForVolumeActor, + setTitleAndDescription, + addDropdownToToolbar, } from '../../../../utils/demo/helpers'; +import * as cornerstoneTools from '@cornerstonejs/tools'; const { ViewportColorBar } = ui.widgets.colorbar; const { ColorBarScalePosition } = ui.widgets.colorbar.Enums; @@ -34,15 +35,17 @@ const { Enums: csToolsEnums, } = cornerstoneTools; -const { MouseBindings } = csToolsEnums; - const { ViewportType } = Enums; +const { MouseBindings, KeyboardBindings } = csToolsEnums; const renderingEngineId = 'myRenderingEngine'; -const stackViewportId = 'CT_STACK'; -const volumeViewportId = 'CT_VOLUME_SAGITTAL'; -const volumeToolGroupId = 'VOLUME_TOOL_GROUP_ID'; +const toolGroupIds = new Set(); +const colorBarWidth = 20; // px +const imageIdsCache = new Map(); + +const wadoRsRoot = 'https://d3t6nz73ql33tx.cloudfront.net/dicomweb'; +const StudyInstanceUID = + '1.3.6.1.4.1.14519.5.2.1.7009.2403.334240657131972136850343327463'; -// Define unique ids for the volumes const volumeLoaderScheme = 'cornerstoneStreamingImageVolume'; // Loader id which defines which volume loader to use const ctVolumeName = 'CT_VOLUME_ID'; // Id of the volume less loader prefix const ctVolumeId = `${volumeLoaderScheme}:${ctVolumeName}`; // VolumeId with loader id + volume id @@ -55,40 +58,84 @@ const colormaps = vtkColormaps.rgbPresetNames.map( (presetName) => vtkColormaps.getPresetByName(presetName) as Colormap ); let currentPTColormapName = 'Black-Body Radiation'; -let ctColorBar = null; -let ptColorBar = null; -// ==[ Set up page ]============================================================ +const viewportsInfo = [ + { + toolGroupId: 'STACK_TOOLGROUP_ID', + fusion: false, + colorBar: { + position: 'right', + instances: [], + }, + viewportInput: { + viewportId: 'CT_STACK_AXIAL', + type: ViewportType.STACK, + element: null, + defaultOptions: { + background: [0.2, 0, 0.2], + }, + }, + }, + { + volumeIds: [ctVolumeId, ptVolumeId], + toolGroupId: 'VOLUME_TOOLGROUP_ID', + fusion: true, + colorBar: { + position: 'right', + instances: [], + }, + viewportInput: { + viewportId: 'CT_VOLUME_SAGITTAL', + type: ViewportType.ORTHOGRAPHIC, + element: null, + defaultOptions: { + orientation: Enums.OrientationAxis.SAGITTAL, + background: [0.2, 0, 0.2], + }, + }, + }, + { + volumeIds: [ctVolumeId, ptVolumeId], + toolGroupId: 'VOLUME_TOOLGROUP_ID', + fusion: true, + colorBar: { + position: 'right', + instances: [], + }, + viewportInput: { + viewportId: 'CT_VOLUME_CORONAL', + type: ViewportType.ORTHOGRAPHIC, + element: null, + defaultOptions: { + orientation: Enums.OrientationAxis.CORONAL, + background: [0.2, 0, 0.2], + }, + }, + }, +]; +// ======== Set up page ======== // setTitleAndDescription( - 'Volume Viewport API With Multiple Volumes', - 'Demonstrates how to interact with a Volume viewport when using fusion.' + 'Color Bar', + 'Interactive color bar that can be used to manipulate the VOI' ); const content = document.getElementById('content'); -const element = document.createElement('div'); -element.id = 'cornerstone-element'; - -Object.assign(element.style, { - position: 'relative', - width: '500px', - height: '500px', - marginBottom: '30px', -}); +const viewportGrid = document.createElement('div'); -content.appendChild(element); +viewportGrid.style.display = 'grid'; +viewportGrid.style.width = '100%'; +viewportGrid.style.height = '500px'; +viewportGrid.style.marginTop = '5px'; +viewportGrid.style.gap = '5px'; -const rightTopContainer = document.createElement('div'); -const rightBottomContainer = document.createElement('div'); -const bottomLeftContainer = document.createElement('div'); -const bottomRightContainer = document.createElement('div'); +// Generate the template columns based on the number of viewports to render. +// The template is set to "auto auto auto" for 3 viewports. +viewportGrid.style.gridTemplateColumns = new Array(viewportsInfo.length) + .fill('auto') + .join(' '); -const containers = [ - rightTopContainer, - rightBottomContainer, - bottomLeftContainer, - bottomRightContainer, -]; +content.appendChild(viewportGrid); const info = document.createElement('div'); content.appendChild(info); @@ -99,184 +146,306 @@ const addInstruction = (instruction) => { info.appendChild(node); }; +addInstruction( + 'Viewports: Stack/Axial (left) | Volume/Sagittal (middle) | Volume/Coronal (right)' +); addInstruction('- Select different colormaps'); -addInstruction('- Click and drag on viewport to change VOI'); -addInstruction('- Click and drag at the color bar to change VOI'); +addInstruction('- Click and drag on the viewport to change VOI'); +addInstruction('- Click and drag on the color bar to change VOI'); -const colorBarSize = { shortSide: 20, longSide: 250 }; +// ==[ Toolbar ]================================================================ -Object.assign(bottomLeftContainer.style, { - position: 'absolute', - top: '100%', - left: '0px', - width: `${colorBarSize.longSide}px`, - height: `${colorBarSize.shortSide}px`, +addDropdownToToolbar({ + options: { + values: colormaps.map((cm) => cm.Name), + defaultValue: currentPTColormapName, + }, + style: { + maxWidth: '200px', + }, + onSelectedValueChange: (selectedValue) => { + setPTColormap(selectedValue); + }, }); -Object.assign(bottomRightContainer.style, { - position: 'absolute', - top: '100%', - left: '50%', - width: `${colorBarSize.longSide}px`, - height: `${colorBarSize.shortSide}px`, -}); +// ============================================================================= -Object.assign(rightTopContainer.style, { - position: 'absolute', - top: '0px', - left: '100%', - width: `${colorBarSize.shortSide}px`, - height: `${colorBarSize.longSide}px`, -}); +function setPTViewportColormap(viewportInfo, colormapName: string) { + const { fusion, colorBar, viewportInput } = viewportInfo; + const { viewportId } = viewportInput; -Object.assign(rightBottomContainer.style, { - position: 'absolute', - top: '50%', - left: '100%', - width: `${colorBarSize.shortSide}px`, - height: `${colorBarSize.longSide}px`, -}); + if (!fusion) { + return; + } -// Change the container style when it has/hasn't a colorbar attached to it -const containersMutationObserver = new MutationObserver((mutations) => { - mutations.forEach((mutation) => { - const container = mutation.target as HTMLElement; - const hasChildNodes = container.hasChildNodes(); + const ptColorBar = colorBar?.instances?.[1]; - Object.assign(container.style, { - display: hasChildNodes ? 'block' : 'none', - border: hasChildNodes ? 'solid 1px #555' : 'none', + if (ptColorBar) { + ptColorBar.activeColormapName = colormapName; + } + + // Get the rendering engine + const renderingEngine = getRenderingEngine(renderingEngineId); + + // Get the volume viewport + const viewport = ( + renderingEngine.getViewport(viewportId) + ); + + viewport.setProperties({ colormap: { name: colormapName } }, ptVolumeId); + viewport.render(); +} + +function setPTColormap(colormapName: string) { + currentPTColormapName = colormapName; + + viewportsInfo.forEach((viewportInfo) => + setPTViewportColormap(viewportInfo, colormapName) + ); +} + +async function createAndCacheVolume(volumeId, imageIds) { + let volume = cache.getVolume(volumeId) as any; + + if (!volume) { + volume = await volumeLoader.createAndCacheVolume(volumeId, { + imageIds, }); - }); -}); -containers.forEach((container) => { - const hasChildNodes = container.hasChildNodes(); + // Set the volume to load + volume.load(); + } +} - Object.assign(container.style, { - boxSizing: 'border-box', - display: hasChildNodes ? 'block' : 'none', - cursor: 'initial', +async function initializeVolumeViewport( + viewportInfo, + viewport: Types.IVolumeViewport +) { + const { fusion } = viewportInfo; + const volumes = []; + const ctImageIds = await getCTImageIds(); + + await createAndCacheVolume(ctVolumeId, ctImageIds); + + volumes.push({ + volumeId: ctVolumeId, + callback: setCtTransferFunctionForVolumeActor, }); - containersMutationObserver.observe(container, { childList: true }); -}); + if (fusion) { + const ptImageIds = await getPTImageIds(); -// ==[ Toolbar ]================================================================ + await createAndCacheVolume(ptVolumeId, ptImageIds); -const orientationOptions = { - axial: 'axial', - sagittal: 'sagittal', - coronal: 'coronal', - oblique: 'oblique', -}; + volumes.push({ + volumeId: ptVolumeId, + callback: setPetColorMapTransferFunctionForVolumeActor, + }); + } -addDropdownToToolbar({ - options: { - values: ['axial', 'sagittal', 'coronal', 'oblique'], - defaultValue: 'sagittal', - }, - onSelectedValueChange: (selectedValue) => { - // Get the rendering engine - const renderingEngine = getRenderingEngine(renderingEngineId); + // Set the volume on the viewport + await viewport.setVolumes(volumes); - // Get the volume viewport - const viewport = ( - renderingEngine.getViewport(volumeViewportId) - ); + if (fusion) { + setPTViewportColormap(viewportInfo, currentPTColormapName); + } +} + +function createRightColorBarContainers(numContainers) { + const containers = []; + const height = 100 / numContainers; + let top = 0; + + for (let i = 0; i < numContainers; i++, top += height) { + const container = document.createElement('div'); + + Object.assign(container.style, { + position: 'absolute', + top: `${top}%`, + left: `calc(100% - ${colorBarWidth}px)`, + width: `${colorBarWidth}px`, + height: `${100 / numContainers}%`, + }); - let viewUp; - let viewPlaneNormal; + containers.push(container); + } - switch (selectedValue) { - case orientationOptions.axial: - viewport.setOrientation(Enums.OrientationAxis.AXIAL); + return containers; +} - break; - case orientationOptions.sagittal: - viewport.setOrientation(Enums.OrientationAxis.SAGITTAL); +function createBottomColorBarContainers(numContainers) { + const containers = []; + const width = 100 / numContainers; + let left = 0; - break; - case orientationOptions.coronal: - viewport.setOrientation(Enums.OrientationAxis.CORONAL); + for (let i = 0; i < numContainers; i++, left += width) { + const container = document.createElement('div'); - break; - case orientationOptions.oblique: - // Some random oblique value for this dataset - viewUp = [-0.5962687530844388, 0.5453181550345819, -0.5891448751239446]; - viewPlaneNormal = [ - -0.5962687530844388, 0.5453181550345819, -0.5891448751239446, - ]; + Object.assign(container.style, { + position: 'absolute', + top: `calc(100% - ${colorBarWidth}px)`, + left: `${left}%`, + width: `${width}%`, + height: `${colorBarWidth}px`, + }); - viewport.setCamera({ viewUp, viewPlaneNormal }); - viewport.resetCamera(); - break; - } + containers.push(container); + } - viewport.render(); - }, -}); + return containers; +} -addDropdownToToolbar({ - options: { - values: colormaps.map((cm) => cm.Name), - defaultValue: currentPTColormapName, - }, - style: { - maxWidth: '200px', - }, - onSelectedValueChange: (selectedValue) => { - setPTColormap(selectedValue); - }, -}); +function initializeColorBarContainers(viewportInfo, viewportContainer) { + const numContainers = viewportInfo.fusion ? 2 : 1; + const containers = + viewportInfo.colorBar?.position === 'right' + ? createRightColorBarContainers(numContainers) + : createBottomColorBarContainers(numContainers); -// ============================================================================= + containers.forEach((container) => { + Object.assign(container.style, { + boxSizing: 'border-box', + display: 'block', + border: 'solid 1px #555', + cursor: 'initial', + }); -function setPTColormap(colormapName: string) { - currentPTColormapName = colormapName; + viewportContainer.appendChild(container); + }); - if (ptColorBar) { - ptColorBar.activeColormapName = colormapName; + return containers; +} + +function initializeColorBars(viewportInfo, colorBarContainers) { + const { fusion, volumeIds = [], colorBar, viewportInput } = viewportInfo; + const { element } = viewportInput; + + const scaleStyle = { + font: '16px Arial', + color: '#fff', + maxNumTicks: 8, + tickSize: 5, + tickWidth: 1, + labelMargin: 3, + }; + + const ctColorBar = new ViewportColorBar({ + id: 'ctColorBar', + element, + container: colorBarContainers[0], + volumeId: volumeIds[0], + colormaps, + activeColormapName: 'Grayscale', + scalePosition: ColorBarScalePosition.TopOrLeft, + scaleStyle, + }); + + colorBar.instances.push(ctColorBar); + + if (fusion && volumeIds.length === 2) { + const ptColorBar = new ViewportColorBar({ + id: 'ptColorBar', + element, + container: colorBarContainers[1], + volumeId: volumeIds[1], + colormaps, + activeColormapName: currentPTColormapName, + scalePosition: ColorBarScalePosition.TopOrLeft, + scaleStyle, + }); + + colorBar.instances.push(ptColorBar); } +} - // Get the rendering engine - const renderingEngine = getRenderingEngine(renderingEngineId); +async function initializeViewport(renderingEngine, toolGroup, viewportInfo) { + const { viewportInput } = viewportInfo; - // Get the volume viewport - const viewport = ( - renderingEngine.getViewport(volumeViewportId) + const viewportContainer = document.createElement('div'); + + Object.assign(viewportContainer.style, { + position: 'relative', + width: '100%', + height: '100%', + border: 'solid 1px #0f0', + }); + + viewportGrid.appendChild(viewportContainer); + + const element = document.createElement('div'); + let width = '100%'; + let height = '100%'; + + if (viewportInfo.colorBar?.position === 'right') { + width = `calc(100% - ${colorBarWidth}px)`; + } else { + height = `calc(100% - ${colorBarWidth}px)`; + } + + // Disable right click context menu so we can have right click tools + element.oncontextmenu = (e) => e.preventDefault(); + + element.id = viewportInput.viewportId; + element.style.overflow = 'hidden'; + + Object.assign(element.style, { + width, + height, + }); + + viewportInput.element = element; + viewportContainer.appendChild(element); + + const { viewportId } = viewportInput; + const { id: renderingEngineId } = renderingEngine; + + renderingEngine.enableElement(viewportInput); + + // Set the tool group on the viewport + toolGroup.addViewport(viewportId, renderingEngineId); + + const colorBarContainers = initializeColorBarContainers( + viewportInfo, + viewportContainer ); - viewport.setProperties({ colormap: { name: colormapName } }, ptVolumeId); - viewport.render(); + initializeColorBars(viewportInfo, colorBarContainers); + + const ctImageIds = await getCTImageIds(); + const viewport = renderingEngine.getViewport(viewportId); + + if (viewportInput.type === ViewportType.STACK) { + (viewport).setStack(ctImageIds); + } else if (viewportInput.type === ViewportType.ORTHOGRAPHIC) { + await initializeVolumeViewport( + viewportInfo, + viewport as Types.IVolumeViewport + ); + } else { + throw new Error('Invalid viewport type'); + } } -/** - * Runs the demo - */ -async function run() { - // Init Cornerstone and related libraries - await initDemo(); +function initializeToolGroup(toolGroupId) { + let toolGroup = ToolGroupManager.getToolGroup(toolGroupId); - // Add tools to Cornerstone3D - cornerstoneTools.addTool(PanTool); - cornerstoneTools.addTool(WindowLevelTool); - cornerstoneTools.addTool(StackScrollMouseWheelTool); - cornerstoneTools.addTool(ZoomTool); + if (toolGroup) { + return toolGroup; + } // Define a tool group, which defines how mouse events map to tool commands for // Any viewport using the group - const volumeToolGroup = ToolGroupManager.createToolGroup(volumeToolGroupId); + toolGroup = ToolGroupManager.createToolGroup(toolGroupId); - // Add tools to the tool group - volumeToolGroup.addTool(WindowLevelTool.toolName); - volumeToolGroup.addTool(PanTool.toolName); - volumeToolGroup.addTool(ZoomTool.toolName); - volumeToolGroup.addTool(StackScrollMouseWheelTool.toolName); + // Add the tools to the tool group + toolGroup.addTool(WindowLevelTool.toolName); + toolGroup.addTool(PanTool.toolName); + toolGroup.addTool(ZoomTool.toolName); + toolGroup.addTool(StackScrollMouseWheelTool.toolName); // Set the initial state of the tools, here all tools are active and bound to // Different mouse inputs - volumeToolGroup.setToolActive(WindowLevelTool.toolName, { + toolGroup.setToolActive(WindowLevelTool.toolName, { bindings: [ { mouseButton: MouseBindings.Primary, // Left Click @@ -284,7 +453,7 @@ async function run() { ], }); - volumeToolGroup.setToolActive(PanTool.toolName, { + toolGroup.setToolActive(PanTool.toolName, { bindings: [ { mouseButton: MouseBindings.Auxiliary, // Middle Click @@ -292,7 +461,7 @@ async function run() { ], }); - volumeToolGroup.setToolActive(ZoomTool.toolName, { + toolGroup.setToolActive(ZoomTool.toolName, { bindings: [ { mouseButton: MouseBindings.Secondary, // Right Click @@ -302,118 +471,70 @@ async function run() { // 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. - volumeToolGroup.setToolActive(StackScrollMouseWheelTool.toolName); - - const wadoRsRoot = 'https://d3t6nz73ql33tx.cloudfront.net/dicomweb'; - const StudyInstanceUID = - '1.3.6.1.4.1.14519.5.2.1.7009.2403.334240657131972136850343327463'; - - // Get Cornerstone imageIds and fetch metadata into RAM - const ctImageIds = await createImageIdsAndCacheMetaData({ - StudyInstanceUID, - SeriesInstanceUID: - '1.3.6.1.4.1.14519.5.2.1.7009.2403.226151125820845824875394858561', - wadoRsRoot, - }); - - const ptImageIds = await createImageIdsAndCacheMetaData({ - StudyInstanceUID, - SeriesInstanceUID: - '1.3.6.1.4.1.14519.5.2.1.7009.2403.879445243400782656317561081015', - wadoRsRoot, - }); - - // Instantiate a rendering engine - const renderingEngine = new RenderingEngine(renderingEngineId); + toolGroup.setToolActive(StackScrollMouseWheelTool.toolName); - // Create a stack viewport - const viewportInput = { - viewportId: volumeViewportId, - type: ViewportType.ORTHOGRAPHIC, - element, - defaultOptions: { - orientation: Enums.OrientationAxis.SAGITTAL, - background: [0.2, 0, 0.2], - }, - }; + return toolGroup; +} - renderingEngine.enableElement(viewportInput); +async function getCTImageIds() { + let imageIds = imageIdsCache.get('ct'); - // Set the tool group on the viewport - volumeToolGroup.addViewport(volumeViewportId, renderingEngineId); + if (!imageIds) { + imageIds = await createImageIdsAndCacheMetaData({ + StudyInstanceUID, + SeriesInstanceUID: + '1.3.6.1.4.1.14519.5.2.1.7009.2403.226151125820845824875394858561', + wadoRsRoot, + }); - // Get the stack viewport that was created - const viewport = ( - renderingEngine.getViewport(volumeViewportId) - ); + imageIdsCache.set('ct', imageIds); + } - // Define a volume in memory - const ctVolume = await volumeLoader.createAndCacheVolume(ctVolumeId, { - imageIds: ctImageIds, - }); + return imageIds; +} - // Set the volume to load - ctVolume.load(); +async function getPTImageIds() { + let imageIds = imageIdsCache.get('pt'); - // Define a volume in memory - const ptVolume = await volumeLoader.createAndCacheVolume(ptVolumeId, { - imageIds: ptImageIds, - }); + if (!imageIds) { + imageIds = await createImageIdsAndCacheMetaData({ + StudyInstanceUID, + SeriesInstanceUID: + '1.3.6.1.4.1.14519.5.2.1.7009.2403.879445243400782656317561081015', + wadoRsRoot, + }); - // Set the volume to load - ptVolume.load(); + imageIdsCache.set('pt', imageIds); + } - // Set the volume on the viewport - await viewport.setVolumes([ - { volumeId: ctVolumeId, callback: setCtTransferFunctionForVolumeActor }, - { - volumeId: ptVolumeId, - callback: setPetColorMapTransferFunctionForVolumeActor, - }, - ]); + return imageIds; +} - setPTColormap(currentPTColormapName); +/** + * Runs the demo + */ +async function run() { + // Init Cornerstone and related libraries + await initDemo(); - // Render the image - renderingEngine.render(); + // Add tools to Cornerstone3D + cornerstoneTools.addTool(PanTool); + cornerstoneTools.addTool(WindowLevelTool); + cornerstoneTools.addTool(StackScrollMouseWheelTool); + cornerstoneTools.addTool(ZoomTool); - // Append the containers after initializing the viewport to keep them over - // all other viewport elements - element.appendChild(rightTopContainer); - element.appendChild(rightBottomContainer); - element.appendChild(bottomLeftContainer); - element.appendChild(bottomRightContainer); + // Instantiate a rendering engine + const renderingEngine = new RenderingEngine(renderingEngineId); - const scaleStyle = { - font: '12px Arial', - color: '#fff', - maxNumTicks: 8, - tickSize: 5, - tickWidth: 1, - labelMargin: 3, - }; + for (let i = 0; i < viewportsInfo.length; i++) { + const viewportInfo = viewportsInfo[i]; + const { toolGroupId } = viewportInfo; + const toolGroup = initializeToolGroup(toolGroupId); - ctColorBar = new ViewportColorBar({ - id: 'ctColorBar', - element, - container: rightTopContainer, - volumeId: ctVolumeId, - colormaps, - activeColormapName: 'Grayscale', - scalePosition: ColorBarScalePosition.TopOrLeft, - scaleStyle, - }); + toolGroupIds.add(toolGroupId); - ptColorBar = new ViewportColorBar({ - id: 'ptColorBar', - element, - container: rightBottomContainer, - volumeId: ptVolumeId, - colormaps, - activeColormapName: currentPTColormapName, - scalePosition: ColorBarScalePosition.TopOrLeft, - scaleStyle, - }); + await initializeViewport(renderingEngine, toolGroup, viewportInfo); + } } run(); diff --git a/packages/core/src/ui/widgets/colorBar/ColorBar.ts b/packages/core/src/ui/widgets/colorBar/ColorBar.ts index e4704697b6..5b88b34fd9 100644 --- a/packages/core/src/ui/widgets/colorBar/ColorBar.ts +++ b/packages/core/src/ui/widgets/colorBar/ColorBar.ts @@ -272,6 +272,7 @@ class ColorBar extends Widget { this.voiRange = newVoiRange; evt.stopPropagation(); + evt.preventDefault(); }; private _mouseUpCallback = (evt) => { diff --git a/packages/core/src/ui/widgets/colorBar/ViewportColorBar.ts b/packages/core/src/ui/widgets/colorBar/ViewportColorBar.ts index 2cee203b09..2e9c93fbcc 100644 --- a/packages/core/src/ui/widgets/colorBar/ViewportColorBar.ts +++ b/packages/core/src/ui/widgets/colorBar/ViewportColorBar.ts @@ -44,13 +44,41 @@ class ViewportColorBar extends ColorBar { return getEnabledElement(this._element); } - protected getVOIMultipliers(): [number, number] { + private get modality() { const { viewport } = this.enabledElement; - // const {actor: volumeActor } = viewport.getActor(this._volumeId) - const volume = cache.getVolume(this._volumeId); - const { scaling } = volume; - const isPreScaled = !!scaling && Object.keys(scaling).length > 0; - const { Modality: modality } = volume.metadata; + + if (viewport instanceof VolumeViewport) { + const volume = cache.getVolume(this._volumeId); + return volume.metadata.Modality; + } + + if (viewport instanceof StackViewport) { + return viewport.modality; + } + + throw new Error('Invalid viewport type'); + } + + private get isPreScaled() { + const { viewport } = this.enabledElement; + + if (viewport instanceof VolumeViewport) { + const volume = cache.getVolume(this._volumeId); + const { scaling } = volume; + + return !!scaling && Object.keys(scaling).length > 0; + } + + if (viewport instanceof StackViewport) { + const { preScale } = viewport.getImageData(); + return preScale.scaled && preScale.scalingParameters?.suvbw !== undefined; + } + + throw new Error('Invalid viewport type'); + } + + protected getVOIMultipliers(): [number, number] { + const { isPreScaled, modality } = this; if (modality === 'PT') { const ptMultiplier = @@ -138,7 +166,6 @@ class ViewportColorBar extends ColorBar { } const { _element: element } = this; - this.range = ViewportColorBar._getRange(element, volumeId); }; @@ -148,7 +175,7 @@ class ViewportColorBar extends ColorBar { const { viewportId, volumeId, range } = evt.detail; const { viewport } = this.enabledElement; - if (viewportId != viewport.id || volumeId !== this._volumeId) { + if (viewportId !== viewport.id || volumeId !== this._volumeId) { return; } From 8cf3d6afe1dee211f1b3ffc9c12de67b5218c453 Mon Sep 17 00:00:00 2001 From: Leonardo Campos Date: Tue, 24 Oct 2023 08:10:45 -0300 Subject: [PATCH 04/14] code review --- packages/core/examples/colorbar/index.ts | 63 ++++-- packages/core/src/ui/widgets/Widget.ts | 98 +++++--- .../core/src/ui/widgets/colorBar/ColorBar.ts | 161 +++++++------- .../src/ui/widgets/colorBar/ColorBarCanvas.ts | 75 ++++--- .../{ColorBarScale.ts => ColorBarTicks.ts} | 209 ++++++++++-------- .../ui/widgets/colorBar/ViewportColorBar.ts | 106 +++------ .../colorBar/common/areColorBarRangesEqual.ts | 10 + .../colorBar/common/areColorBarSizesEqual.ts | 7 + .../src/ui/widgets/colorBar/common/index.ts | 4 + .../colorBar/common/isColorBarSizeValid.ts | 7 + .../widgets/colorBar/common/isRangeValid.ts | 4 +- .../ui/widgets/colorBar/common/isSizeValid.ts | 7 - .../colorBar/common/positionsEquals.ts | 7 - .../ui/widgets/colorBar/common/rangesEqual.ts | 7 - .../ui/widgets/colorBar/common/sizesEqual.ts | 7 - .../enums/ColorBarRangeTextPosition.ts | 11 + .../colorBar/enums/ColorBarScalePosition.ts | 4 - .../src/ui/widgets/colorBar/enums/index.ts | 2 +- .../colorBar/types/ColorBarCanvasProps.ts | 10 +- .../colorBar/types/ColorBarCommonProps.ts | 20 ++ ...ColorBarRange.ts => ColorBarImageRange.ts} | 2 +- .../colorBar/types/ColorBarPosition.ts | 4 - .../widgets/colorBar/types/ColorBarProps.ts | 20 +- .../colorBar/types/ColorBarScaleProps.ts | 17 -- .../colorBar/types/ColorBarTicksProps.ts | 9 + ...BarScaleStyle.ts => ColorBarTicksStyle.ts} | 4 +- .../colorBar/types/ColorBarVOIRange.ts | 4 +- .../src/ui/widgets/colorBar/types/Colormap.ts | 5 - .../colorBar/types/ColormapCanvasProps.ts | 10 +- .../colorBar/types/ViewportColorBarProps.ts | 6 + .../src/ui/widgets/colorBar/types/index.ts | 12 +- .../core/src/ui/widgets/types/WidgetProps.ts | 4 + .../core/src/ui/widgets/types/WidgetSize.ts | 4 + packages/core/src/ui/widgets/types/index.ts | 2 + .../MultiTargetEventListenerManager.ts | 90 ++++++++ .../eventListener/TargetEventListeners.ts} | 115 +++++----- .../core/src/utilities/eventListener/index.ts | 2 + .../core/src/utilities/getVOIMultipliers.ts | 33 +++ .../core/src/utilities/getViewportModality.ts | 23 ++ packages/core/src/utilities/index.ts | 8 + .../core/src/utilities/isViewportPreScaled.ts | 19 ++ utils/ExampleRunner/example-info.json | 4 + 42 files changed, 753 insertions(+), 463 deletions(-) rename packages/core/src/ui/widgets/colorBar/{ColorBarScale.ts => ColorBarTicks.ts} (62%) create mode 100644 packages/core/src/ui/widgets/colorBar/common/areColorBarRangesEqual.ts create mode 100644 packages/core/src/ui/widgets/colorBar/common/areColorBarSizesEqual.ts create mode 100644 packages/core/src/ui/widgets/colorBar/common/index.ts create mode 100644 packages/core/src/ui/widgets/colorBar/common/isColorBarSizeValid.ts delete mode 100644 packages/core/src/ui/widgets/colorBar/common/isSizeValid.ts delete mode 100644 packages/core/src/ui/widgets/colorBar/common/positionsEquals.ts delete mode 100644 packages/core/src/ui/widgets/colorBar/common/rangesEqual.ts delete mode 100644 packages/core/src/ui/widgets/colorBar/common/sizesEqual.ts create mode 100644 packages/core/src/ui/widgets/colorBar/enums/ColorBarRangeTextPosition.ts delete mode 100644 packages/core/src/ui/widgets/colorBar/enums/ColorBarScalePosition.ts create mode 100644 packages/core/src/ui/widgets/colorBar/types/ColorBarCommonProps.ts rename packages/core/src/ui/widgets/colorBar/types/{ColorBarRange.ts => ColorBarImageRange.ts} (51%) delete mode 100644 packages/core/src/ui/widgets/colorBar/types/ColorBarPosition.ts delete mode 100644 packages/core/src/ui/widgets/colorBar/types/ColorBarScaleProps.ts create mode 100644 packages/core/src/ui/widgets/colorBar/types/ColorBarTicksProps.ts rename packages/core/src/ui/widgets/colorBar/types/{ColorBarScaleStyle.ts => ColorBarTicksStyle.ts} (75%) delete mode 100644 packages/core/src/ui/widgets/colorBar/types/Colormap.ts create mode 100644 packages/core/src/ui/widgets/colorBar/types/ViewportColorBarProps.ts create mode 100644 packages/core/src/ui/widgets/types/WidgetProps.ts create mode 100644 packages/core/src/ui/widgets/types/WidgetSize.ts create mode 100644 packages/core/src/ui/widgets/types/index.ts create mode 100644 packages/core/src/utilities/eventListener/MultiTargetEventListenerManager.ts rename packages/core/src/{ui/widgets/colorBar/EventListeners.ts => utilities/eventListener/TargetEventListeners.ts} (73%) create mode 100644 packages/core/src/utilities/eventListener/index.ts create mode 100644 packages/core/src/utilities/getVOIMultipliers.ts create mode 100644 packages/core/src/utilities/getViewportModality.ts create mode 100644 packages/core/src/utilities/isViewportPreScaled.ts diff --git a/packages/core/examples/colorbar/index.ts b/packages/core/examples/colorbar/index.ts index cd3031e3dd..cd5df4057f 100644 --- a/packages/core/examples/colorbar/index.ts +++ b/packages/core/examples/colorbar/index.ts @@ -19,7 +19,7 @@ import { import * as cornerstoneTools from '@cornerstonejs/tools'; const { ViewportColorBar } = ui.widgets.colorbar; -const { ColorBarScalePosition } = ui.widgets.colorbar.Enums; +const { ColorBarRangeTextPosition } = ui.widgets.colorbar.Enums; // This is for debugging purposes console.warn( @@ -36,7 +36,7 @@ const { } = cornerstoneTools; const { ViewportType } = Enums; -const { MouseBindings, KeyboardBindings } = csToolsEnums; +const { MouseBindings } = csToolsEnums; const renderingEngineId = 'myRenderingEngine'; const toolGroupIds = new Set(); const colorBarWidth = 20; // px @@ -54,11 +54,20 @@ const ctVolumeId = `${volumeLoaderScheme}:${ctVolumeName}`; // VolumeId with loa const ptVolumeName = 'PT_VOLUME_ID'; const ptVolumeId = `${volumeLoaderScheme}:${ptVolumeName}`; +// Convert all VTK colormaps to the one supported by the colorbar which actualy +// have almost the same properties. const colormaps = vtkColormaps.rgbPresetNames.map( - (presetName) => vtkColormaps.getPresetByName(presetName) as Colormap + (presetName) => + vtkColormaps.getPresetByName( + presetName + ) as unknown as Types.ColormapRegistration ); + +// Colormap to load right after loading the example page but it can be changed +// after selecting a different one from the dropdown. let currentPTColormapName = 'Black-Body Radiation'; +// Info about all the three viewports (stack, volume sagittal and volume coronal) const viewportsInfo = [ { toolGroupId: 'STACK_TOOLGROUP_ID', @@ -123,6 +132,7 @@ setTitleAndDescription( const content = document.getElementById('content'); const viewportGrid = document.createElement('div'); +// Grid were all viewports are added in a single row viewportGrid.style.display = 'grid'; viewportGrid.style.width = '100%'; viewportGrid.style.height = '500px'; @@ -155,6 +165,7 @@ addInstruction('- Click and drag on the color bar to change VOI'); // ==[ Toolbar ]================================================================ +// Dropdown that allows the user to select a different colormap addDropdownToToolbar({ options: { values: colormaps.map((cm) => cm.Name), @@ -170,6 +181,7 @@ addDropdownToToolbar({ // ============================================================================= +// Change the colormap of an specific viewport function setPTViewportColormap(viewportInfo, colormapName: string) { const { fusion, colorBar, viewportInput } = viewportInfo; const { viewportId } = viewportInput; @@ -196,6 +208,7 @@ function setPTViewportColormap(viewportInfo, colormapName: string) { viewport.render(); } +// Change the colormap of all viewports function setPTColormap(colormapName: string) { currentPTColormapName = colormapName; @@ -232,6 +245,7 @@ async function initializeVolumeViewport( callback: setCtTransferFunctionForVolumeActor, }); + // Add PT volume on fusion viewports if (fusion) { const ptImageIds = await getPTImageIds(); @@ -246,11 +260,13 @@ async function initializeVolumeViewport( // Set the volume on the viewport await viewport.setVolumes(volumes); + // Update the colormap to the active one on PT/CT viewports if (fusion) { setPTViewportColormap(viewportInfo, currentPTColormapName); } } +// Creates one or more containers at the right side of the viewport function createRightColorBarContainers(numContainers) { const containers = []; const height = 100 / numContainers; @@ -273,6 +289,7 @@ function createRightColorBarContainers(numContainers) { return containers; } +// Creates one or more containers at the bottom of the viewport function createBottomColorBarContainers(numContainers) { const containers = []; const width = 100 / numContainers; @@ -295,6 +312,8 @@ function createBottomColorBarContainers(numContainers) { return containers; } +// Creates one or more containers at the right side or at the bottom +// of the viewport based on `colorBar.position` config function initializeColorBarContainers(viewportInfo, viewportContainer) { const numContainers = viewportInfo.fusion ? 2 : 1; const containers = @@ -316,12 +335,13 @@ function initializeColorBarContainers(viewportInfo, viewportContainer) { return containers; } +// Create instaces of the color bars for CT or PT/CT viewports and add them to the DOM function initializeColorBars(viewportInfo, colorBarContainers) { const { fusion, volumeIds = [], colorBar, viewportInput } = viewportInfo; const { element } = viewportInput; const scaleStyle = { - font: '16px Arial', + font: '12px Arial', color: '#fff', maxNumTicks: 8, tickSize: 5, @@ -336,8 +356,8 @@ function initializeColorBars(viewportInfo, colorBarContainers) { volumeId: volumeIds[0], colormaps, activeColormapName: 'Grayscale', - scalePosition: ColorBarScalePosition.TopOrLeft, - scaleStyle, + rangeTextPosition: ColorBarRangeTextPosition.TopOrLeft, + ticksStyle: scaleStyle, }); colorBar.instances.push(ctColorBar); @@ -350,17 +370,30 @@ function initializeColorBars(viewportInfo, colorBarContainers) { volumeId: volumeIds[1], colormaps, activeColormapName: currentPTColormapName, - scalePosition: ColorBarScalePosition.TopOrLeft, - scaleStyle, + rangeTextPosition: ColorBarRangeTextPosition.TopOrLeft, + ticksStyle: scaleStyle, }); colorBar.instances.push(ptColorBar); } } +/** + * Creates a viewport, load its stack/volume and adds its color bar(s). + * HTML Structure: + * viewportGrid + * viewportContainer + * viewport + * canvas + * color bar container (CT) + * color bar (CT) + * color bar container (PT) + * color bar (PT) + */ async function initializeViewport(renderingEngine, toolGroup, viewportInfo) { const { viewportInput } = viewportInfo; + // Container where the viewport and the color bars are added to const viewportContainer = document.createElement('div'); Object.assign(viewportContainer.style, { @@ -376,6 +409,8 @@ async function initializeViewport(renderingEngine, toolGroup, viewportInfo) { let width = '100%'; let height = '100%'; + // Leave some space for the color bar that can be added to the + // left or at the bottom of the viewport if (viewportInfo.colorBar?.position === 'right') { width = `calc(100% - ${colorBarWidth}px)`; } else { @@ -386,12 +421,7 @@ async function initializeViewport(renderingEngine, toolGroup, viewportInfo) { element.oncontextmenu = (e) => e.preventDefault(); element.id = viewportInput.viewportId; - element.style.overflow = 'hidden'; - - Object.assign(element.style, { - width, - height, - }); + Object.assign(element.style, { width, height, overflow: 'hidden' }); viewportInput.element = element; viewportContainer.appendChild(element); @@ -399,16 +429,20 @@ async function initializeViewport(renderingEngine, toolGroup, viewportInfo) { const { viewportId } = viewportInput; const { id: renderingEngineId } = renderingEngine; + // Enable the viewport renderingEngine.enableElement(viewportInput); // Set the tool group on the viewport toolGroup.addViewport(viewportId, renderingEngineId); + // Create the color bar containers that will be used to append the + // colorBars' rootElement const colorBarContainers = initializeColorBarContainers( viewportInfo, viewportContainer ); + // Create and add the color bars to the DOM initializeColorBars(viewportInfo, colorBarContainers); const ctImageIds = await getCTImageIds(); @@ -426,6 +460,7 @@ async function initializeViewport(renderingEngine, toolGroup, viewportInfo) { } } +// Create a tool group and add all necessary tools to it function initializeToolGroup(toolGroupId) { let toolGroup = ToolGroupManager.getToolGroup(toolGroupId); diff --git a/packages/core/src/ui/widgets/Widget.ts b/packages/core/src/ui/widgets/Widget.ts index d619b44d41..0a8d6e1bb6 100644 --- a/packages/core/src/ui/widgets/Widget.ts +++ b/packages/core/src/ui/widgets/Widget.ts @@ -1,14 +1,47 @@ -export type WidgetProps = { - id: string; - container?: HTMLElement; -}; - -type WidgetSize = { - width: number; - height: number; -}; - -class Widget { +import type { WidgetProps, WidgetSize } from './types'; + +/** + * Base class for any widget that can be added to cornerstone. Currently it is + * responsible only for holding the `rootElement`, contains a method that allows + * adding it to the DOM and it also listens to container's size changes when the + * widget is already added to the DOM. `dispose` must be called to destroy the + * widget because it removes the widget from the DOM and stop listening to + * container changes. + * + * You can apply some styles to widgets using the widget id or the `widget` class. + * + * Example: + * type ColorPickerProps = WidgetProps & { + * selectedColor: string; + * } + * + * class ColorPicker extends Widget { + * constructor(props: ColorPickerProps) { + * super(props); + * // [code] + * } + * + * public show() { + * console.log('Show color picker'); + * } + * + * protected containerResized() { + * console.log('New container size: ', this.containerSize); + * } + * } + * + * const colorPicker = new ColorPicker({ + * container: document.body, + * selectedColor: '#000'; + * }); + * + * // another way to add the color picker to the DOM + * colorPicker.appendTo(document.body) + * + * // Show color picker + * colorPicker.show(); + */ +abstract class Widget { private _id: string; private _rootElement: HTMLElement; private _containerSize: WidgetSize; @@ -17,7 +50,7 @@ class Widget { constructor({ id, container }: WidgetProps) { this._id = id; this._containerSize = { width: 0, height: 0 }; - this._rootElement = this.createRootElement(); + this._rootElement = this.createRootElement(id); this._containerResizeObserver = new ResizeObserver( this._containerResizeCallback ); @@ -38,9 +71,12 @@ class Widget { return this._rootElement; } - protected createRootElement(): HTMLElement { + protected createRootElement(id: string): HTMLElement { const rootElement = document.createElement('div'); + rootElement.id = id; + rootElement.classList.add('widget'); + Object.assign(rootElement.style, { width: '100%', height: '100%', @@ -50,8 +86,8 @@ class Widget { } /** - * Append the color bar node to a parent element and re-renders the color bar - * @param container - HTML element where the color bar will be added to + * Append the widget to a parent element + * @param container - HTML element where the widget should be added to */ public appendTo(container: HTMLElement) { const { @@ -72,6 +108,20 @@ class Widget { resizeObserver.observe(container); } + /** + * Removes the widget from the DOM and stop listening to DOM events + */ + public destroy() { + const { + _rootElement: rootElement, + _containerResizeObserver: resizeObserver, + } = this; + const { parentElement } = rootElement; + + parentElement?.removeChild(rootElement); + resizeObserver.disconnect(); + } + protected get containerSize(): WidgetSize { // Returns a copy to prevent any external change return { ...this._containerSize }; @@ -81,7 +131,7 @@ class Widget { * Method called every time widget's container is resize giving the * opportunity to children classes to act when that happens. */ - protected containerResized() { + protected onContainerResize() { // no-op } @@ -104,22 +154,8 @@ class Widget { } this._containerSize = { width, height }; - this.containerResized(); + this.onContainerResize(); }; - - /** - * Removes the widget from the DOM and stop listening to DOM events - */ - public dispose() { - const { - _rootElement: rootElement, - _containerResizeObserver: resizeObserver, - } = this; - const { parentElement } = rootElement; - - parentElement?.removeChild(rootElement); - resizeObserver.disconnect(); - } } export { Widget as default, Widget }; diff --git a/packages/core/src/ui/widgets/colorBar/ColorBar.ts b/packages/core/src/ui/widgets/colorBar/ColorBar.ts index 5b88b34fd9..dcf18117ba 100644 --- a/packages/core/src/ui/widgets/colorBar/ColorBar.ts +++ b/packages/core/src/ui/widgets/colorBar/ColorBar.ts @@ -1,17 +1,19 @@ import { vec2 } from 'gl-matrix'; import { utilities, Types } from '@cornerstonejs/core'; -import { EventListenersManager } from './EventListeners'; import { Widget } from '../Widget'; -import { ColorBarProps, ColorBarVOIRange, Colormap } from './types'; -import ColorBarCanvas from './ColorBarCanvas'; -import ColorBarScale from './ColorBarScale'; -import isRangeValid from './common/isRangeValid'; -import rangesEqual from './common/rangesEqual'; -import { ColorBarScalePosition } from './enums/ColorBarScalePosition'; - -const DEFAULT_MULTIPLIER = 1; -const DEFAULT_SCALE_BAR_POSITION = ColorBarScalePosition.BottomOrRight; -const SCALE_BAR_SIZE = 50; +import type { ColorBarProps, ColorBarVOIRange } from './types'; +import { isRangeValid, areColorBarRangesEqual } from './common'; +import { ColorBarRangeTextPosition } from './enums/ColorBarRangeTextPosition'; +import { ColorBarCanvas } from './ColorBarCanvas'; +import ColorBarTicks from './ColorBarTicks'; + +const { MultiTargetEventListenerManager } = utilities.eventListener; + +const DEFAULTS = { + MULTIPLIER: 1, + RANGE_TEXT_POSITION: ColorBarRangeTextPosition.BottomOrRight, + TICKS_BAR_SIZE: 50, +}; type ColorBarPoints = { page: Types.Point2; @@ -20,26 +22,27 @@ type ColorBarPoints = { }; class ColorBar extends Widget { - private _colormaps: Map; + private _colormaps: Map; private _activeColormapName: string; - private _eventListenersManager: EventListenersManager; + private _eventListenersManager: MultiTargetEventListenerManager; private _canvas: ColorBarCanvas; - private _scaleBar: ColorBarScale; - private _scalePosition: ColorBarScalePosition; + private _rangeText: ColorBarTicks; + private _rangeTextPosition: ColorBarRangeTextPosition; private _isInteracting = false; constructor(props: ColorBarProps) { super(props); - this._eventListenersManager = new EventListenersManager(); + this._eventListenersManager = new MultiTargetEventListenerManager(); this._colormaps = ColorBar.getColormapsMap(props); this._activeColormapName = ColorBar.getInitialColormapName(props); this._canvas = this._createCanvas(props); - this._scaleBar = this._createScaleBar(props); - this._scalePosition = props.scalePosition ?? DEFAULT_SCALE_BAR_POSITION; + this._rangeText = this._createTicksBar(props); + this._rangeTextPosition = + props.rangeTextPosition ?? DEFAULTS.RANGE_TEXT_POSITION; this._canvas.appendTo(this.rootElement); - this._scaleBar.appendTo(document.body); + this._rangeText.appendTo(document.body); this._addRootElementEventListeners(); } @@ -49,7 +52,7 @@ class ColorBar extends Widget { return colormaps.reduce( (items, item) => items.set(item.Name, item), - new Map() + new Map() ); } @@ -88,13 +91,13 @@ class ColorBar extends Widget { this._canvas.colormap = colormap; } - public get range() { - return this._canvas.range; + public get imageRange() { + return this._canvas.imageRange; } - public set range(range: ColorBarVOIRange) { - this._canvas.range = range; - this._scaleBar.range = range; + public set imageRange(imageRange: ColorBarVOIRange) { + this._canvas.imageRange = imageRange; + this._rangeText.imageRange = imageRange; } public get voiRange() { @@ -104,27 +107,30 @@ class ColorBar extends Widget { public set voiRange(voiRange: ColorBarVOIRange) { const { voiRange: currentVoiRange } = this._canvas; - if (!isRangeValid(voiRange) || rangesEqual(voiRange, currentVoiRange)) { + if ( + !isRangeValid(voiRange) || + areColorBarRangesEqual(voiRange, currentVoiRange) + ) { return; } this._canvas.voiRange = voiRange; - this._scaleBar.voiRange = voiRange; - this.voiChanged(voiRange); + this._rangeText.voiRange = voiRange; + this.onVoiChange(voiRange); } - public get showFullPixelValueRange() { - return this._canvas.showFullPixelValueRange; + public get showFullImageRange() { + return this._canvas.showFullImageRange; } - public set showFullPixelValueRange(value: boolean) { - this._canvas.showFullPixelValueRange = value; - this._scaleBar.showFullPixelValueRange = value; + public set showFullImageRange(value: boolean) { + this._canvas.showFullImageRange = value; + this._rangeText.showFullPixelValueRange = value; } - public dispose() { - super.dispose(); - this._removeRootElementEventListeners(); + public destroy() { + super.destroy(); + this._eventListenersManager.reset(); } protected createRootElement(): HTMLElement { @@ -138,37 +144,37 @@ class ColorBar extends Widget { return rootElement; } - protected containerResized() { - super.containerResized(); + protected onContainerResize() { + super.onContainerResize(); this._canvas.size = this.containerSize; } protected getVOIMultipliers(): [number, number] { - return [DEFAULT_MULTIPLIER, DEFAULT_MULTIPLIER]; + return [DEFAULTS.MULTIPLIER, DEFAULTS.MULTIPLIER]; } - protected voiChanged(voiRange: ColorBarVOIRange) { - // TODO: override voiRange property? + protected onVoiChange(voiRange: ColorBarVOIRange) { + // no-op } private _createCanvas(props: ColorBarProps) { - const { range, voiRange, showFullPixelValueRange } = props; + const { imageRange, voiRange, showFullPixelValueRange } = props; const colormap = this._colormaps.get(this._activeColormapName); return new ColorBarCanvas({ colormap, - range: range, + imageRange, voiRange: voiRange, showFullPixelValueRange, }); } - public _createScaleBar(props: ColorBarProps): ColorBarScale { - return new ColorBarScale({ - range: props.range, + public _createTicksBar(props: ColorBarProps): ColorBarTicks { + return new ColorBarTicks({ + imageRange: props.imageRange, voiRange: props.voiRange, - scaleStyle: props.scaleStyle, - scalePosition: props.scalePosition, + ticksStyle: props.ticksStyle, + rangeTextPosition: props.rangeTextPosition, showFullPixelValueRange: props.showFullPixelValueRange, }); } @@ -186,49 +192,50 @@ class ColorBar extends Widget { return { client: clientPoint, page: pagePoint, local: localPoints }; } - private showScaleBar() { - const { _scaleBar: scaleBar } = this; + private showTicksBar() { + const { _rangeText: ticksBar } = this; const { width: containerWidth, height: containerHeight } = this.containerSize; const { top: containerTop, left: containerLeft } = this.rootElement.getBoundingClientRect(); const isHorizontal = containerWidth >= containerHeight; - const width = isHorizontal ? containerWidth : SCALE_BAR_SIZE; - const height = isHorizontal ? SCALE_BAR_SIZE : containerHeight; + const width = isHorizontal ? containerWidth : DEFAULTS.TICKS_BAR_SIZE; + const height = isHorizontal ? DEFAULTS.TICKS_BAR_SIZE : containerHeight; - let scaleBarTop; - let scaleBarLeft; + let ticksBarTop; + let ticksBarLeft; - scaleBar.size = { width, height }; + ticksBar.size = { width, height }; if (isHorizontal) { - scaleBarTop = - this._scalePosition === ColorBarScalePosition.TopOrLeft + ticksBarTop = + this._rangeTextPosition === ColorBarRangeTextPosition.TopOrLeft ? containerTop - height : containerTop + containerHeight; - scaleBarLeft = containerLeft; + ticksBarLeft = containerLeft; } else { - scaleBarTop = containerTop; + ticksBarTop = containerTop; - scaleBarLeft = - this._scalePosition === ColorBarScalePosition.TopOrLeft + ticksBarLeft = + this._rangeTextPosition === ColorBarRangeTextPosition.TopOrLeft ? containerLeft - width : containerLeft + containerWidth; } - scaleBar.position = { top: scaleBarTop, left: scaleBarLeft }; - scaleBar.visible = true; + ticksBar.top = ticksBarTop; + ticksBar.left = ticksBarLeft; + ticksBar.visible = true; } private _mouseOverCallback = (evt) => { - this.showScaleBar(); + this.showTicksBar(); evt.stopPropagation(); }; private _mouseOutCallback = (evt) => { if (!this._isInteracting) { - this._scaleBar.visible = false; + this._rangeText.visible = false; } evt.stopPropagation(); }; @@ -282,20 +289,12 @@ class ColorBar extends Widget { }; private _addRootElementEventListeners() { + const { _eventListenersManager: manager } = this; const { rootElement: element } = this; - this._removeRootElementEventListeners(); - element.addEventListener('mouseover', this._mouseOverCallback); - element.addEventListener('mouseout', this._mouseOutCallback); - element.addEventListener('mousedown', this._mouseDownCallback); - } - - private _removeRootElementEventListeners() { - const { rootElement: element } = this; - - element.removeEventListener('mouseover', this._mouseOverCallback); - element.removeEventListener('mouseout', this._mouseOutCallback); - element.removeEventListener('mousedown', this._mouseDownCallback); + manager.addEventListener(element, 'mouseover', this._mouseOverCallback); + manager.addEventListener(element, 'mouseout', this._mouseOutCallback); + manager.addEventListener(element, 'mousedown', this._mouseDownCallback); } private _addVOIEventListeners(evt: MouseEvent) { @@ -306,8 +305,8 @@ class ColorBar extends Widget { this._removeVOIEventListeners(); - document.addEventListener('mouseup', this._mouseUpCallback); - manager.addEventListener(document, 'colorbar.voi.mousemove', (evt) => + manager.addEventListener(document, 'voi.mouseup', this._mouseUpCallback); + manager.addEventListener(document, 'voi.mousemove', (evt) => this._mouseDragCallback(evt, initialDragState) ); } @@ -315,8 +314,8 @@ class ColorBar extends Widget { private _removeVOIEventListeners() { const { _eventListenersManager: manager } = this; - document.removeEventListener('mouseup', this._mouseUpCallback); - manager.removeEventListener(document, 'colorbar.voi.mousemove'); + manager.removeEventListener(document, 'voi.mouseup'); + manager.removeEventListener(document, 'voi.mousemove'); } } diff --git a/packages/core/src/ui/widgets/colorBar/ColorBarCanvas.ts b/packages/core/src/ui/widgets/colorBar/ColorBarCanvas.ts index b188af583e..bfaea2fbb0 100644 --- a/packages/core/src/ui/widgets/colorBar/ColorBarCanvas.ts +++ b/packages/core/src/ui/widgets/colorBar/ColorBarCanvas.ts @@ -1,11 +1,13 @@ import { utilities } from '@cornerstonejs/core'; -import { ColorBarRange, ColorBarVOIRange, Colormap } from './types'; import { ColorBarCanvasProps } from './types/ColorBarCanvasProps'; -import { ColorBarSize } from './types/ColorBarSize'; -import isRangeValid from './common/isRangeValid'; -import rangesEqual from './common/rangesEqual'; -import isSizeValid from './common/isSizeValid'; -import sizesEqual from './common/sizesEqual'; +import type { ColorBarImageRange, ColorBarVOIRange, Colormap } from './types'; +import type { ColorBarSize } from './types/ColorBarSize'; +import { + isRangeValid, + areColorBarRangesEqual, + isColorBarSizeValid, + areColorBarSizesEqual, +} from './common'; const clamp = (value, min, max) => Math.min(Math.max(min, value), max); @@ -17,12 +19,16 @@ const interpolateVec3 = (a, b, t) => { ]; }; +/** + * Canvas referenced by the color bar where the colormap is rendered. It may + * show the full image range or only the VOI range. + */ class ColorBarCanvas { private _canvas: HTMLCanvasElement; - private _range: ColorBarRange; + private _imageRange: ColorBarImageRange; private _voiRange: ColorBarVOIRange; private _colormap: Colormap; - private _showFullPixelValueRange: boolean; + private _showFullImageRange: boolean; constructor(props: ColorBarCanvasProps) { ColorBarCanvas.validateProps(props); @@ -30,16 +36,16 @@ class ColorBarCanvas { const { colormap, size = { width: 20, height: 100 }, - range = { lower: 0, upper: 1 }, + imageRange = { lower: 0, upper: 1 }, voiRange = { lower: 0, upper: 1 }, container, showFullPixelValueRange = false, } = props; this._colormap = colormap; - this._range = range; + this._imageRange = imageRange; this._voiRange = voiRange; - this._showFullPixelValueRange = showFullPixelValueRange; + this._showFullImageRange = showFullPixelValueRange; this._canvas = this._createRootElement(size); if (container) { @@ -64,7 +70,7 @@ class ColorBarCanvas { public set size(size: ColorBarSize) { const { _canvas: canvas } = this; - if (!isSizeValid(size) || sizesEqual(canvas, size)) { + if (!isColorBarSizeValid(size) || areColorBarSizesEqual(canvas, size)) { return; } @@ -72,16 +78,19 @@ class ColorBarCanvas { this.render(); } - public get range(): ColorBarRange { - return { ...this._range }; + public get imageRange(): ColorBarImageRange { + return { ...this._imageRange }; } - public set range(range: ColorBarRange) { - if (!isRangeValid(range) || rangesEqual(range, this._range)) { + public set imageRange(imageRange: ColorBarImageRange) { + if ( + !isRangeValid(imageRange) || + areColorBarRangesEqual(imageRange, this._imageRange) + ) { return; } - this._range = range; + this._imageRange = imageRange; this.render(); } @@ -90,7 +99,10 @@ class ColorBarCanvas { } public set voiRange(voiRange: ColorBarVOIRange) { - if (!isRangeValid(voiRange) || rangesEqual(voiRange, this._voiRange)) { + if ( + !isRangeValid(voiRange) || + areColorBarRangesEqual(voiRange, this._voiRange) + ) { return; } @@ -98,16 +110,16 @@ class ColorBarCanvas { this.render(); } - public get showFullPixelValueRange(): boolean { - return this._showFullPixelValueRange; + public get showFullImageRange(): boolean { + return this._showFullImageRange; } - public set showFullPixelValueRange(showFullRange: boolean) { - if (showFullRange === this._showFullPixelValueRange) { + public set showFullImageRange(showFullImageRange: boolean) { + if (showFullImageRange === this._showFullImageRange) { return; } - this._showFullPixelValueRange = showFullRange; + this._showFullImageRange = showFullImageRange; this.render(); } @@ -124,14 +136,14 @@ class ColorBarCanvas { } private static validateProps(props: ColorBarCanvasProps) { - const { size, range, voiRange } = props; + const { size, imageRange, voiRange } = props; - if (size && !isSizeValid(size)) { + if (size && !isColorBarSizeValid(size)) { throw new Error('Invalid "size"'); } - if (range && !isRangeValid(range)) { - throw new Error('Invalid "range"'); + if (imageRange && !isRangeValid(imageRange)) { + throw new Error('Invalid "imageRange"'); } if (voiRange && !isRangeValid(voiRange)) { @@ -173,9 +185,15 @@ class ColorBarCanvas { const { RGBPoints: rgbPoints } = colormap; const colorsCount = rgbPoints.length / 4; + // Returns a color point from rgbPoints. Each point has position, red, + // green and blue components which means each point has an offset equal + // to `4 * index` const getColorPoint = (index) => { const offset = 4 * index; + // It can get out of bounds when `voiRange.upper` is smaller than + // `imageRange.upper`. It's also checking if is smaller than zero + // for safety only because that should never happens. if (index < 0 || index >= colorsCount) { return; } @@ -196,7 +214,7 @@ class ColorBarCanvas { const isHorizontal = width > height; const maxValue = isHorizontal ? width : height; const { _voiRange: voiRange } = this; - const range = this._showFullPixelValueRange ? this._range : { ...voiRange }; + const range = this._showFullImageRange ? this._imageRange : { ...voiRange }; const { windowWidth } = utilities.windowLevel.toWindowLevel( voiRange.lower, @@ -206,6 +224,7 @@ class ColorBarCanvas { let previousColorPoint = undefined; let currentColorPoint = getColorPoint(0); + // Starts from `range.lower` incrementing by incRawPixelValue on each iteration const incRawPixelValue = (range.upper - range.lower) / (maxValue - 1); let rawPixelValue = range.lower; diff --git a/packages/core/src/ui/widgets/colorBar/ColorBarScale.ts b/packages/core/src/ui/widgets/colorBar/ColorBarTicks.ts similarity index 62% rename from packages/core/src/ui/widgets/colorBar/ColorBarScale.ts rename to packages/core/src/ui/widgets/colorBar/ColorBarTicks.ts index a93a01455c..56195d0c4f 100644 --- a/packages/core/src/ui/widgets/colorBar/ColorBarScale.ts +++ b/packages/core/src/ui/widgets/colorBar/ColorBarTicks.ts @@ -1,60 +1,69 @@ -import { ColorBarRange } from './types/ColorBarRange'; -import { ColorBarVOIRange } from './types/ColorBarVOIRange'; -import { ColorBarSize } from './types/ColorBarSize'; -import { ColorBarScaleProps } from './types/ColorBarScaleProps'; -import { ColorBarPosition } from './types/ColorBarPosition'; -import isSizeValid from './common/isSizeValid'; -import isRangeValid from './common/isRangeValid'; -import rangesEqual from './common/rangesEqual'; -import sizesEqual from './common/sizesEqual'; -import positionsEqual from './common/positionsEquals'; -import { ColorBarScalePosition } from './enums/ColorBarScalePosition'; - -const DEFAULT_FONT = '10px Arial'; -const DEFAULT_COLOR = 'white'; -const DEFAULT_TICK_SIZE = 5; -const DEFAULT_TICK_WIDTH = 1; -const DEFAULT_TICK_LABEL_MARGIN = 3; -const DEFAULT_MAX_NUM_TICKS = 8; - -class ColorBarScale { +import type { + ColorBarImageRange, + ColorBarVOIRange, + ColorBarSize, + ColorBarTicksProps, +} from './types'; +import { + isColorBarSizeValid, + isRangeValid, + areColorBarRangesEqual, + areColorBarSizesEqual, +} from './common'; +import { ColorBarRangeTextPosition } from './enums/ColorBarRangeTextPosition'; + +const DEFAULTS = { + FONT: '10px Arial', + COLOR: 'white', + TICK_SIZE: 5, + TICK_WIDTH: 1, + TICK_LABEL_MARGIN: 3, + MAX_NUM_TICKS: 8, + + // Must start with 1 and end with 10 + TICKS_STEPS: [1, 2.5, 5, 10], +}; + +class ColorBarTicks { private _canvas: HTMLCanvasElement; - private _range: ColorBarRange; + private _imageRange: ColorBarImageRange; private _voiRange: ColorBarVOIRange; private _color: string; private _tickSize: number; private _tickWidth: number; private _labelMargin: number; private _maxNumTicks: number; - private _scalePosition: ColorBarScalePosition; + private _rangeTextPosition: ColorBarRangeTextPosition; private _showFullPixelValueRange: boolean; private _font: string; - constructor(props: ColorBarScaleProps) { - ColorBarScale.validateProps(props); + constructor(props: ColorBarTicksProps) { + ColorBarTicks.validateProps(props); const { + top = 0, + left = 0, size = { width: 20, height: 100 }, - position = { top: 0, left: 0 }, - range = { lower: 0, upper: 1 }, + imageRange = { lower: 0, upper: 1 }, voiRange = { lower: 0, upper: 1 }, - scaleStyle, - scalePosition, + ticksStyle, + rangeTextPosition, container, showFullPixelValueRange = false, } = props; - this._range = range; + this._imageRange = imageRange; this._voiRange = voiRange; - this._font = scaleStyle?.font ?? DEFAULT_FONT; - this._color = scaleStyle?.color ?? DEFAULT_COLOR; - this._tickSize = scaleStyle?.tickSize ?? DEFAULT_TICK_SIZE; - this._tickWidth = scaleStyle?.tickWidth ?? DEFAULT_TICK_WIDTH; - this._labelMargin = scaleStyle?.labelMargin ?? DEFAULT_TICK_LABEL_MARGIN; - this._maxNumTicks = scaleStyle?.maxNumTicks ?? DEFAULT_MAX_NUM_TICKS; - this._scalePosition = scalePosition ?? ColorBarScalePosition.TopOrLeft; + this._font = ticksStyle?.font ?? DEFAULTS.FONT; + this._color = ticksStyle?.color ?? DEFAULTS.COLOR; + this._tickSize = ticksStyle?.tickSize ?? DEFAULTS.TICK_SIZE; + this._tickWidth = ticksStyle?.tickWidth ?? DEFAULTS.TICK_WIDTH; + this._labelMargin = ticksStyle?.labelMargin ?? DEFAULTS.TICK_LABEL_MARGIN; + this._maxNumTicks = ticksStyle?.maxNumTicks ?? DEFAULTS.MAX_NUM_TICKS; + this._rangeTextPosition = + rangeTextPosition ?? ColorBarRangeTextPosition.TopOrLeft; this._showFullPixelValueRange = showFullPixelValueRange; - this._canvas = this._createCanvasElement(size, position); + this._canvas = this._createCanvasElement(size, top, left); if (container) { this.appendTo(container); @@ -69,7 +78,7 @@ class ColorBarScale { public set size(size: ColorBarSize) { const { _canvas: canvas } = this; - if (!isSizeValid(size) || sizesEqual(canvas, size)) { + if (!isColorBarSizeValid(size) || areColorBarSizesEqual(canvas, size)) { return; } @@ -77,32 +86,51 @@ class ColorBarScale { this.render(); } - public get position(): ColorBarPosition { - return this._getCanvasPosition(this._canvas); + public get top(): number { + return Number.parseInt(this._canvas.style.top); } - public set position(position: ColorBarPosition) { + public set top(top: number) { const { _canvas: canvas } = this; - const currentPosition = this._getCanvasPosition(canvas); + const currentTop = this.top; - if (positionsEqual(position, currentPosition)) { + if (top === currentTop) { return; } - this._setCanvasPosition(canvas, position); + canvas.style.top = `${top}px`; this.render(); } - public get range() { - return { ...this._range }; + public get left(): number { + return Number.parseInt(this._canvas.style.left); } - public set range(range: ColorBarVOIRange) { - if (!isRangeValid(range) || rangesEqual(range, this._range)) { + public set left(left: number) { + const { _canvas: canvas } = this; + const currentLeft = this.left; + + if (left === currentLeft) { return; } - this._range = range; + canvas.style.left = `${left}px`; + this.render(); + } + + public get imageRange() { + return { ...this._imageRange }; + } + + public set imageRange(imageRange: ColorBarVOIRange) { + if ( + !isRangeValid(imageRange) || + areColorBarRangesEqual(imageRange, this._imageRange) + ) { + return; + } + + this._imageRange = imageRange; this.render(); } @@ -111,7 +139,10 @@ class ColorBarScale { } public set voiRange(voiRange: ColorBarVOIRange) { - if (!isRangeValid(voiRange) || rangesEqual(voiRange, this._voiRange)) { + if ( + !isRangeValid(voiRange) || + areColorBarRangesEqual(voiRange, this._voiRange) + ) { return; } @@ -192,15 +223,15 @@ class ColorBarScale { this.render(); } - private static validateProps(props: ColorBarScaleProps) { - const { size, range, voiRange } = props; + private static validateProps(props: ColorBarTicksProps) { + const { size, imageRange, voiRange } = props; - if (size && !isSizeValid(size)) { + if (size && !isColorBarSizeValid(size)) { throw new Error('Invalid "size"'); } - if (range && !isRangeValid(range)) { - throw new Error('Invalid "range"'); + if (imageRange && !isRangeValid(imageRange)) { + throw new Error('Invalid "imageRange"'); } if (voiRange && !isRangeValid(voiRange)) { @@ -220,27 +251,10 @@ class ColorBarScale { }); } - private _getCanvasPosition(canvas): ColorBarPosition { - const { top: canvasTop, left: canvasLeft } = this._canvas.style; - const top = Number.parseInt(canvasTop); - const left = Number.parseInt(canvasLeft); - - return { top, left }; - } - - private _setCanvasPosition( - canvas: HTMLCanvasElement, - position: ColorBarPosition - ) { - Object.assign(canvas.style, { - top: `${position.top}px`, - left: `${position.left}px`, - }); - } - private _createCanvasElement( size: ColorBarSize, - position: ColorBarPosition + top: number, + left: number ): HTMLCanvasElement { const canvas = document.createElement('canvas'); @@ -248,43 +262,58 @@ class ColorBarScale { display: 'none', position: 'absolute', boxSizing: 'border-box', + top: `${top}px`, + left: `${left}px`, }); this._setCanvasSize(canvas, size); - this._setCanvasPosition(canvas, position); return canvas; } /** - * Calculate "ticks" to be displayed for the current range + * Calculate how many ticks can be displayed on the screen based on the + * pre-defined steps (`TICKS_STEPS`) as follow: + * 1. Calculate what should be the step (`roughStep`) based on the range and + * the number of desired steps (`maxNumTicks`). + * 2. Find a number power of 10 (eg: 0.1, 1, 10, 100, etc.) that can be used + * to multiply `roughStep` and return a number between 1 and 10 which is + * called `roughtStepNormalized`. + * 3. Find in the TICKS_STEPS array a number that is bigger than or equal to + * the `roughtStepNormalized` value (`normalizedStep`). + * 4. Multiply the `normalizedStep` to move it to the real range. + * * @param range - Range with "lower" and "upper" values */ - private _getTicks(range: ColorBarRange) { + private _getTicks(range) { const { lower, upper } = range; const rangeValue = upper - lower; - // First approximation + // First approximation based on the max number of ticks const roughStep = rangeValue / (this._maxNumTicks - 1); - // Set best step for the range - const goodNormalizedSteps = [1, 2, 5, 10]; - // Normalize rough step to find the normalized one that fits best const stepPower = Math.pow( 10, -Math.floor(Math.log10(Math.abs(roughStep))) ); - const normalizedStep = roughStep * stepPower; - const goodNormalizedStep = goodNormalizedSteps.find( - (n) => n >= normalizedStep + + // Get a number between 1 and 10 + const roughtStepNormalized = roughStep * stepPower; + + // Find a normalize step that is greater than or equal to `roughtStepNormalized` + const normalizedStep = DEFAULTS.TICKS_STEPS.find( + (n) => n >= roughtStepNormalized ); - const step = goodNormalizedStep / stepPower; + + // Move `normalizedStep` to the real range + const step = normalizedStep / stepPower; // Determine the scale limits based on the chosen step. const scaleMax = Math.ceil(upper / step) * step; const scaleMin = Math.floor(lower / step) * step; + // Find a possible tick values for the `step` computed const ticksCount = Math.round((scaleMax - scaleMin) / step) + 1; const ticks = []; @@ -338,7 +367,9 @@ class ColorBarScale { const maxCanvasPixelValue = isHorizontal ? width : height; const canvasContext = canvas.getContext('2d'); const { _voiRange: voiRange } = this; - const range = this._showFullPixelValueRange ? this._range : { ...voiRange }; + const range = this._showFullPixelValueRange + ? this._imageRange + : { ...voiRange }; const rangeWidth = range.upper - range.lower; const { ticks } = this._getTicks(range); @@ -368,13 +399,13 @@ class ColorBarScale { let tickInfo; if (isHorizontal) { - if (this._scalePosition === ColorBarScalePosition.TopOrLeft) { + if (this._rangeTextPosition === ColorBarRangeTextPosition.TopOrLeft) { tickInfo = this._getTopTickInfo({ position, labelMeasure }); } else { tickInfo = this._getBottomTickInfo({ position, labelMeasure }); } } else { - if (this._scalePosition === ColorBarScalePosition.TopOrLeft) { + if (this._rangeTextPosition === ColorBarRangeTextPosition.TopOrLeft) { tickInfo = this._getLeftTickInfo({ position, labelMeasure }); } else { tickInfo = this._getRightTickInfo({ position }); @@ -395,4 +426,4 @@ class ColorBarScale { } } -export { ColorBarScale as default, ColorBarScale as ColorBarVOIScale }; +export { ColorBarTicks as default, ColorBarTicks }; diff --git a/packages/core/src/ui/widgets/colorBar/ViewportColorBar.ts b/packages/core/src/ui/widgets/colorBar/ViewportColorBar.ts index 2e9c93fbcc..5ad86434c6 100644 --- a/packages/core/src/ui/widgets/colorBar/ViewportColorBar.ts +++ b/packages/core/src/ui/widgets/colorBar/ViewportColorBar.ts @@ -6,32 +6,26 @@ import { Enums, utilities, getEnabledElement, - cache, } from '@cornerstonejs/core'; import { ColorBar } from './ColorBar'; -import { ColorBarProps, ColorBarVOIRange } from './types'; +import type { ViewportColorBarProps, ColorBarVOIRange } from './types'; const { Events } = Enums; -const DEFAULT_MULTIPLIER = 4; - -export interface ViewportColorBarProps extends ColorBarProps { - element: HTMLDivElement; - volumeId?: string; -} +const defaultImageRange = { lower: -1000, upper: 1000 }; class ViewportColorBar extends ColorBar { private _element: HTMLDivElement; private _volumeId: string; constructor(props: ViewportColorBarProps) { - super({ - ...props, - range: ViewportColorBar._getRange(props.element, props.volumeId), - voiRange: ViewportColorBar._getVOIRange(props.element, props.volumeId), - }); + const { element, volumeId } = props; + const imageRange = ViewportColorBar._getImageRange(element, volumeId); + const voiRange = ViewportColorBar._getVOIRange(element, volumeId); - this._element = props.element; - this._volumeId = props.volumeId; + super({ ...props, imageRange, voiRange }); + + this._element = element; + this._volumeId = volumeId; this._addCornerstoneEventListener(); } @@ -44,54 +38,13 @@ class ViewportColorBar extends ColorBar { return getEnabledElement(this._element); } - private get modality() { - const { viewport } = this.enabledElement; - - if (viewport instanceof VolumeViewport) { - const volume = cache.getVolume(this._volumeId); - return volume.metadata.Modality; - } - - if (viewport instanceof StackViewport) { - return viewport.modality; - } - - throw new Error('Invalid viewport type'); - } - - private get isPreScaled() { - const { viewport } = this.enabledElement; - - if (viewport instanceof VolumeViewport) { - const volume = cache.getVolume(this._volumeId); - const { scaling } = volume; - - return !!scaling && Object.keys(scaling).length > 0; - } - - if (viewport instanceof StackViewport) { - const { preScale } = viewport.getImageData(); - return preScale.scaled && preScale.scalingParameters?.suvbw !== undefined; - } - - throw new Error('Invalid viewport type'); - } - protected getVOIMultipliers(): [number, number] { - const { isPreScaled, modality } = this; - - if (modality === 'PT') { - const ptMultiplier = - 5 / Math.max(this.containerSize.width, this.containerSize.height); - - return isPreScaled ? [0, ptMultiplier] : [0, DEFAULT_MULTIPLIER]; - } - - return [DEFAULT_MULTIPLIER, DEFAULT_MULTIPLIER]; + const { viewport } = this.enabledElement; + return utilities.getVOIMultipliers(viewport, this._volumeId); } - protected voiChanged(voiRange: ColorBarVOIRange) { - super.voiChanged(voiRange); + protected onVoiChange(voiRange: ColorBarVOIRange) { + super.onVoiChange(voiRange); const { viewport } = this.enabledElement; @@ -112,8 +65,7 @@ class ViewportColorBar extends ColorBar { } } - private static _getRange(element, volumeId) { - const defaultValue = { lower: -1000, upper: 1000 }; + private static _getImageRange(element, volumeId?) { const enabledElement = getEnabledElement(element); const { viewport } = enabledElement; @@ -122,19 +74,18 @@ class ViewportColorBar extends ColorBar { : viewport.getDefaultActor(); if (!actor) { - return defaultValue; + return defaultImageRange; } const imageData = actor.actor.getMapper().getInputData(); - const range = imageData.getPointData().getScalars().getRange(); + const imageRange = imageData.getPointData().getScalars().getRange(); - return range[0] === 0 && range[1] === 0 - ? defaultValue - : { lower: range[0], upper: range[1] }; + return imageRange[0] === 0 && imageRange[1] === 0 + ? defaultImageRange + : { lower: imageRange[0], upper: imageRange[1] }; } private static _getVOIRange(element, volumeId) { - const defaultValue = { lower: -1000, upper: 1000 }; const enabledElement = getEnabledElement(element); const { viewport } = enabledElement; @@ -143,7 +94,7 @@ class ViewportColorBar extends ColorBar { : viewport.getDefaultActor(); if (!actor) { - return { lower: -1000, upper: 1000 }; + return defaultImageRange; } const voiRange = actor.actor @@ -152,10 +103,14 @@ class ViewportColorBar extends ColorBar { .getRange(); return voiRange[0] === 0 && voiRange[1] === 0 - ? defaultValue + ? defaultImageRange : { lower: voiRange[0], upper: voiRange[1] }; } + private _stackNewImageCallback = () => { + this.imageRange = ViewportColorBar._getImageRange(this._element); + }; + private _imageVolumeModifiedCallback = ( evt: Types.EventTypes.ImageVolumeModifiedEvent ) => { @@ -166,20 +121,20 @@ class ViewportColorBar extends ColorBar { } const { _element: element } = this; - this.range = ViewportColorBar._getRange(element, volumeId); + this.imageRange = ViewportColorBar._getImageRange(element, volumeId); }; private _viewportVOIModifiedCallback = ( evt: Types.EventTypes.VoiModifiedEvent ) => { - const { viewportId, volumeId, range } = evt.detail; + const { viewportId, volumeId, range: voiRange } = evt.detail; const { viewport } = this.enabledElement; if (viewportId !== viewport.id || volumeId !== this._volumeId) { return; } - this.voiRange = range; + this.voiRange = voiRange; }; private _addCornerstoneEventListener() { @@ -190,6 +145,11 @@ class ViewportColorBar extends ColorBar { this._imageVolumeModifiedCallback ); + element.addEventListener( + Events.STACK_NEW_IMAGE, + this._stackNewImageCallback + ); + element.addEventListener( Events.VOI_MODIFIED, this._viewportVOIModifiedCallback diff --git a/packages/core/src/ui/widgets/colorBar/common/areColorBarRangesEqual.ts b/packages/core/src/ui/widgets/colorBar/common/areColorBarRangesEqual.ts new file mode 100644 index 0000000000..2c6250e2cf --- /dev/null +++ b/packages/core/src/ui/widgets/colorBar/common/areColorBarRangesEqual.ts @@ -0,0 +1,10 @@ +import type { ColorBarImageRange } from '../types/ColorBarImageRange'; + +const areColorBarRangesEqual = ( + a: ColorBarImageRange, + b: ColorBarImageRange +) => { + return !!a && !!b && a.lower === b.lower && a.upper === b.upper; +}; + +export { areColorBarRangesEqual as default, areColorBarRangesEqual }; diff --git a/packages/core/src/ui/widgets/colorBar/common/areColorBarSizesEqual.ts b/packages/core/src/ui/widgets/colorBar/common/areColorBarSizesEqual.ts new file mode 100644 index 0000000000..da68bb816c --- /dev/null +++ b/packages/core/src/ui/widgets/colorBar/common/areColorBarSizesEqual.ts @@ -0,0 +1,7 @@ +import type { ColorBarSize } from '../types/ColorBarSize'; + +const areColorBarSizesEqual = (a: ColorBarSize, b: ColorBarSize) => { + return !!a && !!b && a.width === b.width && a.height === b.height; +}; + +export { areColorBarSizesEqual as default, areColorBarSizesEqual }; diff --git a/packages/core/src/ui/widgets/colorBar/common/index.ts b/packages/core/src/ui/widgets/colorBar/common/index.ts new file mode 100644 index 0000000000..a85bfd6122 --- /dev/null +++ b/packages/core/src/ui/widgets/colorBar/common/index.ts @@ -0,0 +1,4 @@ +export { isRangeValid } from './isRangeValid'; +export { isColorBarSizeValid } from './isColorBarSizeValid'; +export { areColorBarRangesEqual } from './areColorBarRangesEqual'; +export { areColorBarSizesEqual } from './areColorBarSizesEqual'; diff --git a/packages/core/src/ui/widgets/colorBar/common/isColorBarSizeValid.ts b/packages/core/src/ui/widgets/colorBar/common/isColorBarSizeValid.ts new file mode 100644 index 0000000000..01ffe359c2 --- /dev/null +++ b/packages/core/src/ui/widgets/colorBar/common/isColorBarSizeValid.ts @@ -0,0 +1,7 @@ +import type { ColorBarSize } from '../types/ColorBarSize'; + +const isColorBarSizeValid = (size: ColorBarSize) => { + return !!size && size.width > 0 && size.height > 0; +}; + +export { isColorBarSizeValid as default, isColorBarSizeValid }; diff --git a/packages/core/src/ui/widgets/colorBar/common/isRangeValid.ts b/packages/core/src/ui/widgets/colorBar/common/isRangeValid.ts index 65895444a4..71a0a505c9 100644 --- a/packages/core/src/ui/widgets/colorBar/common/isRangeValid.ts +++ b/packages/core/src/ui/widgets/colorBar/common/isRangeValid.ts @@ -1,6 +1,6 @@ -import { ColorBarRange } from '../types/ColorBarRange'; +import type { ColorBarImageRange } from '../types/ColorBarImageRange'; -const isRangeValid = (range: ColorBarRange) => { +const isRangeValid = (range: ColorBarImageRange) => { return range && range.upper > range.lower; }; diff --git a/packages/core/src/ui/widgets/colorBar/common/isSizeValid.ts b/packages/core/src/ui/widgets/colorBar/common/isSizeValid.ts deleted file mode 100644 index 23b5a97e7a..0000000000 --- a/packages/core/src/ui/widgets/colorBar/common/isSizeValid.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { ColorBarSize } from '../types/ColorBarSize'; - -const isSizeValid = (size: ColorBarSize) => { - return !!size && size.width > 0 && size.height > 0; -}; - -export { isSizeValid as default, isSizeValid }; diff --git a/packages/core/src/ui/widgets/colorBar/common/positionsEquals.ts b/packages/core/src/ui/widgets/colorBar/common/positionsEquals.ts deleted file mode 100644 index 3b0d0f79c8..0000000000 --- a/packages/core/src/ui/widgets/colorBar/common/positionsEquals.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { ColorBarPosition } from '../types/ColorBarPosition'; - -const positionsEqual = (a: ColorBarPosition, b: ColorBarPosition) => { - return !!a && !!b && a.left === b.left && a.top === b.top; -}; - -export { positionsEqual as default, positionsEqual }; diff --git a/packages/core/src/ui/widgets/colorBar/common/rangesEqual.ts b/packages/core/src/ui/widgets/colorBar/common/rangesEqual.ts deleted file mode 100644 index 91e87500d1..0000000000 --- a/packages/core/src/ui/widgets/colorBar/common/rangesEqual.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { ColorBarRange } from '../types/ColorBarRange'; - -const rangesEqual = (a: ColorBarRange, b: ColorBarRange) => { - return !!a && !!b && a.lower === b.lower && a.upper === b.upper; -}; - -export { rangesEqual as default, rangesEqual }; diff --git a/packages/core/src/ui/widgets/colorBar/common/sizesEqual.ts b/packages/core/src/ui/widgets/colorBar/common/sizesEqual.ts deleted file mode 100644 index f4cd303e78..0000000000 --- a/packages/core/src/ui/widgets/colorBar/common/sizesEqual.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { ColorBarSize } from '../types/ColorBarSize'; - -const sizesEqual = (a: ColorBarSize, b: ColorBarSize) => { - return !!a && !!b && a.width === b.width && a.height === b.height; -}; - -export { sizesEqual as default, sizesEqual }; diff --git a/packages/core/src/ui/widgets/colorBar/enums/ColorBarRangeTextPosition.ts b/packages/core/src/ui/widgets/colorBar/enums/ColorBarRangeTextPosition.ts new file mode 100644 index 0000000000..ee450e3b81 --- /dev/null +++ b/packages/core/src/ui/widgets/colorBar/enums/ColorBarRangeTextPosition.ts @@ -0,0 +1,11 @@ +/** + * Specify the position of the text/ticks that are displayed when the user + * interacts with the color bar. The position of the text depends on the + * position of the color bar as follow: + * - TopOrLeft: horizontal/top OR vertical/left + * - BottomOrRight: horizontal/bottom OR vertical/right + */ +export enum ColorBarRangeTextPosition { + TopOrLeft, + BottomOrRight, +} diff --git a/packages/core/src/ui/widgets/colorBar/enums/ColorBarScalePosition.ts b/packages/core/src/ui/widgets/colorBar/enums/ColorBarScalePosition.ts deleted file mode 100644 index 2b1c3a9593..0000000000 --- a/packages/core/src/ui/widgets/colorBar/enums/ColorBarScalePosition.ts +++ /dev/null @@ -1,4 +0,0 @@ -export enum ColorBarScalePosition { - TopOrLeft, - BottomOrRight, -} diff --git a/packages/core/src/ui/widgets/colorBar/enums/index.ts b/packages/core/src/ui/widgets/colorBar/enums/index.ts index efd1f14d3d..6ebc46ba34 100644 --- a/packages/core/src/ui/widgets/colorBar/enums/index.ts +++ b/packages/core/src/ui/widgets/colorBar/enums/index.ts @@ -1 +1 @@ -export { ColorBarScalePosition } from './ColorBarScalePosition'; +export { ColorBarRangeTextPosition } from './ColorBarRangeTextPosition'; diff --git a/packages/core/src/ui/widgets/colorBar/types/ColorBarCanvasProps.ts b/packages/core/src/ui/widgets/colorBar/types/ColorBarCanvasProps.ts index 2b78175be0..ebd5e94005 100644 --- a/packages/core/src/ui/widgets/colorBar/types/ColorBarCanvasProps.ts +++ b/packages/core/src/ui/widgets/colorBar/types/ColorBarCanvasProps.ts @@ -1,12 +1,12 @@ -import { ColorBarRange } from './ColorBarRange'; -import { ColorBarSize } from './ColorBarSize'; -import { ColorBarVOIRange } from './ColorBarVOIRange'; -import { Colormap } from './Colormap'; +import type { ColorBarImageRange } from './ColorBarImageRange'; +import type { ColorBarSize } from './ColorBarSize'; +import type { ColorBarVOIRange } from './ColorBarVOIRange'; +import type { Colormap } from './Colormap'; export interface ColorBarCanvasProps { colormap: Colormap; size?: ColorBarSize; - range?: ColorBarRange; + imageRange?: ColorBarImageRange; voiRange?: ColorBarVOIRange; container?: HTMLElement; diff --git a/packages/core/src/ui/widgets/colorBar/types/ColorBarCommonProps.ts b/packages/core/src/ui/widgets/colorBar/types/ColorBarCommonProps.ts new file mode 100644 index 0000000000..2cb2765eaa --- /dev/null +++ b/packages/core/src/ui/widgets/colorBar/types/ColorBarCommonProps.ts @@ -0,0 +1,20 @@ +import { ColorBarRangeTextPosition } from '../enums/ColorBarRangeTextPosition'; +import type { + ColorBarImageRange, + ColorBarTicksStyle, + ColorBarVOIRange, +} from '.'; + +export type ColorBarCommonProps = { + // Image range from minPixelValue (lower) to maxPixelValue (upper) + imageRange?: ColorBarImageRange; + // VOI Range that is related to Window Width and Window Center + voiRange?: ColorBarVOIRange; + // Position where the range text should be displayed related to the ticks bar + rangeTextPosition?: ColorBarRangeTextPosition; + // Ticks style + ticksStyle?: ColorBarTicksStyle; + // The color bar shall show a range from `imageRange.lower` to `imageRange.upper` + // when it is set to `true` or from `voiRange.lower` to `voiRange.upper` otherwise. + showFullPixelValueRange?: boolean; +}; diff --git a/packages/core/src/ui/widgets/colorBar/types/ColorBarRange.ts b/packages/core/src/ui/widgets/colorBar/types/ColorBarImageRange.ts similarity index 51% rename from packages/core/src/ui/widgets/colorBar/types/ColorBarRange.ts rename to packages/core/src/ui/widgets/colorBar/types/ColorBarImageRange.ts index f32760b901..4561ec842d 100644 --- a/packages/core/src/ui/widgets/colorBar/types/ColorBarRange.ts +++ b/packages/core/src/ui/widgets/colorBar/types/ColorBarImageRange.ts @@ -1,4 +1,4 @@ -export type ColorBarRange = { +export type ColorBarImageRange = { lower: number; upper: number; }; diff --git a/packages/core/src/ui/widgets/colorBar/types/ColorBarPosition.ts b/packages/core/src/ui/widgets/colorBar/types/ColorBarPosition.ts deleted file mode 100644 index bcb7cedd88..0000000000 --- a/packages/core/src/ui/widgets/colorBar/types/ColorBarPosition.ts +++ /dev/null @@ -1,4 +0,0 @@ -export type ColorBarPosition = { - top: number; - left: number; -}; diff --git a/packages/core/src/ui/widgets/colorBar/types/ColorBarProps.ts b/packages/core/src/ui/widgets/colorBar/types/ColorBarProps.ts index 22ea9abb3f..bb9c775d58 100644 --- a/packages/core/src/ui/widgets/colorBar/types/ColorBarProps.ts +++ b/packages/core/src/ui/widgets/colorBar/types/ColorBarProps.ts @@ -1,16 +1,8 @@ -import { WidgetProps } from '../../Widget'; -import { ColorBarScalePosition } from '../enums/ColorBarScalePosition'; -import { ColorBarRange } from './ColorBarRange'; -import { ColorBarScaleStyle } from './ColorBarScaleStyle'; -import { ColorBarVOIRange } from './ColorBarVOIRange'; -import { Colormap } from './Colormap'; +import { Types } from '@cornerstonejs/core'; +import type { WidgetProps } from '../../types'; +import { ColorBarCommonProps } from '.'; -export interface ColorBarProps extends WidgetProps { - colormaps: Colormap[]; +export type ColorBarProps = (WidgetProps & ColorBarCommonProps) & { + colormaps: Types.ColormapRegistration[]; activeColormapName?: string; - range?: ColorBarRange; - voiRange?: ColorBarVOIRange; - scalePosition?: ColorBarScalePosition; - scaleStyle?: ColorBarScaleStyle; - showFullPixelValueRange?: boolean; -} +}; diff --git a/packages/core/src/ui/widgets/colorBar/types/ColorBarScaleProps.ts b/packages/core/src/ui/widgets/colorBar/types/ColorBarScaleProps.ts deleted file mode 100644 index 9356a719ab..0000000000 --- a/packages/core/src/ui/widgets/colorBar/types/ColorBarScaleProps.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { ColorBarScalePosition } from '../enums/ColorBarScalePosition'; -import { ColorBarPosition } from './ColorBarPosition'; -import { ColorBarRange } from './ColorBarRange'; -import { ColorBarScaleStyle } from './ColorBarScaleStyle'; -import { ColorBarSize } from './ColorBarSize'; -import { ColorBarVOIRange } from './ColorBarVOIRange'; - -export interface ColorBarScaleProps { - size?: ColorBarSize; - position?: ColorBarPosition; - range: ColorBarRange; - voiRange: ColorBarVOIRange; - scaleStyle?: ColorBarScaleStyle; - scalePosition?: ColorBarScalePosition; - container?: HTMLElement; - showFullPixelValueRange?: boolean; -} diff --git a/packages/core/src/ui/widgets/colorBar/types/ColorBarTicksProps.ts b/packages/core/src/ui/widgets/colorBar/types/ColorBarTicksProps.ts new file mode 100644 index 0000000000..d670fbde20 --- /dev/null +++ b/packages/core/src/ui/widgets/colorBar/types/ColorBarTicksProps.ts @@ -0,0 +1,9 @@ +import type { ColorBarCommonProps, ColorBarSize, ColorBarTicksStyle } from '.'; + +export type ColorBarTicksProps = ColorBarCommonProps & { + top?: number; + left?: number; + size?: ColorBarSize; + ticksStyle?: ColorBarTicksStyle; + container?: HTMLElement; +}; diff --git a/packages/core/src/ui/widgets/colorBar/types/ColorBarScaleStyle.ts b/packages/core/src/ui/widgets/colorBar/types/ColorBarTicksStyle.ts similarity index 75% rename from packages/core/src/ui/widgets/colorBar/types/ColorBarScaleStyle.ts rename to packages/core/src/ui/widgets/colorBar/types/ColorBarTicksStyle.ts index 93441daa0c..95bfca3503 100644 --- a/packages/core/src/ui/widgets/colorBar/types/ColorBarScaleStyle.ts +++ b/packages/core/src/ui/widgets/colorBar/types/ColorBarTicksStyle.ts @@ -1,8 +1,8 @@ -export interface ColorBarScaleStyle { +export type ColorBarTicksStyle = { font?: string; color?: string; tickSize?: number; tickWidth?: number; labelMargin?: number; maxNumTicks?: number; -} +}; diff --git a/packages/core/src/ui/widgets/colorBar/types/ColorBarVOIRange.ts b/packages/core/src/ui/widgets/colorBar/types/ColorBarVOIRange.ts index 7435869d61..2eafbb62c0 100644 --- a/packages/core/src/ui/widgets/colorBar/types/ColorBarVOIRange.ts +++ b/packages/core/src/ui/widgets/colorBar/types/ColorBarVOIRange.ts @@ -1,3 +1,3 @@ -import type { ColorBarRange } from './ColorBarRange'; +import type { ColorBarImageRange } from './ColorBarImageRange'; -export type ColorBarVOIRange = ColorBarRange; +export type ColorBarVOIRange = ColorBarImageRange; diff --git a/packages/core/src/ui/widgets/colorBar/types/Colormap.ts b/packages/core/src/ui/widgets/colorBar/types/Colormap.ts deleted file mode 100644 index 734279a52f..0000000000 --- a/packages/core/src/ui/widgets/colorBar/types/Colormap.ts +++ /dev/null @@ -1,5 +0,0 @@ -export type Colormap = { - ColorSpace: string; - Name: string; - RGBPoints: number[]; -}; diff --git a/packages/core/src/ui/widgets/colorBar/types/ColormapCanvasProps.ts b/packages/core/src/ui/widgets/colorBar/types/ColormapCanvasProps.ts index 2b78175be0..ebd5e94005 100644 --- a/packages/core/src/ui/widgets/colorBar/types/ColormapCanvasProps.ts +++ b/packages/core/src/ui/widgets/colorBar/types/ColormapCanvasProps.ts @@ -1,12 +1,12 @@ -import { ColorBarRange } from './ColorBarRange'; -import { ColorBarSize } from './ColorBarSize'; -import { ColorBarVOIRange } from './ColorBarVOIRange'; -import { Colormap } from './Colormap'; +import type { ColorBarImageRange } from './ColorBarImageRange'; +import type { ColorBarSize } from './ColorBarSize'; +import type { ColorBarVOIRange } from './ColorBarVOIRange'; +import type { Colormap } from './Colormap'; export interface ColorBarCanvasProps { colormap: Colormap; size?: ColorBarSize; - range?: ColorBarRange; + imageRange?: ColorBarImageRange; voiRange?: ColorBarVOIRange; container?: HTMLElement; diff --git a/packages/core/src/ui/widgets/colorBar/types/ViewportColorBarProps.ts b/packages/core/src/ui/widgets/colorBar/types/ViewportColorBarProps.ts new file mode 100644 index 0000000000..ab7f590322 --- /dev/null +++ b/packages/core/src/ui/widgets/colorBar/types/ViewportColorBarProps.ts @@ -0,0 +1,6 @@ +import type { ColorBarProps } from './ColorBarProps'; + +export type ViewportColorBarProps = ColorBarProps & { + element: HTMLDivElement; + volumeId?: string; +}; diff --git a/packages/core/src/ui/widgets/colorBar/types/index.ts b/packages/core/src/ui/widgets/colorBar/types/index.ts index 40ec91d559..31f0cdcac9 100644 --- a/packages/core/src/ui/widgets/colorBar/types/index.ts +++ b/packages/core/src/ui/widgets/colorBar/types/index.ts @@ -1,4 +1,8 @@ -export { ColorBarProps } from './ColorBarProps'; -export { ColorBarRange } from './ColorBarRange'; -export { ColorBarVOIRange } from './ColorBarVOIRange'; -export { Colormap } from './Colormap'; +export type { ColorBarCommonProps } from './ColorBarCommonProps'; +export type { ColorBarProps } from './ColorBarProps'; +export type { ColorBarImageRange } from './ColorBarImageRange'; +export type { ColorBarVOIRange } from './ColorBarVOIRange'; +export type { ColorBarSize } from './ColorBarSize'; +export type { ColorBarTicksProps } from './ColorBarTicksProps'; +export type { ColorBarTicksStyle } from './ColorBarTicksStyle'; +export type { ViewportColorBarProps } from './ViewportColorBarProps'; diff --git a/packages/core/src/ui/widgets/types/WidgetProps.ts b/packages/core/src/ui/widgets/types/WidgetProps.ts new file mode 100644 index 0000000000..7877841996 --- /dev/null +++ b/packages/core/src/ui/widgets/types/WidgetProps.ts @@ -0,0 +1,4 @@ +export type WidgetProps = { + id: string; + container?: HTMLElement; +}; diff --git a/packages/core/src/ui/widgets/types/WidgetSize.ts b/packages/core/src/ui/widgets/types/WidgetSize.ts new file mode 100644 index 0000000000..aaf045ac20 --- /dev/null +++ b/packages/core/src/ui/widgets/types/WidgetSize.ts @@ -0,0 +1,4 @@ +export type WidgetSize = { + width: number; + height: number; +}; diff --git a/packages/core/src/ui/widgets/types/index.ts b/packages/core/src/ui/widgets/types/index.ts new file mode 100644 index 0000000000..7d7244cfb1 --- /dev/null +++ b/packages/core/src/ui/widgets/types/index.ts @@ -0,0 +1,2 @@ +export { WidgetProps } from './WidgetProps'; +export { WidgetSize } from './WidgetSize'; diff --git a/packages/core/src/utilities/eventListener/MultiTargetEventListenerManager.ts b/packages/core/src/utilities/eventListener/MultiTargetEventListenerManager.ts new file mode 100644 index 0000000000..a45255d5fe --- /dev/null +++ b/packages/core/src/utilities/eventListener/MultiTargetEventListenerManager.ts @@ -0,0 +1,90 @@ +import TargetEventListeners from './TargetEventListeners'; + +/** + * MultiTargetEventListenerManager allows you to add event listeners to multiple + * HTML elements (targets) with support for event types with namespace, + * allow removing events without having to pass a callback and makes it possible + * to remove all event lsiteners from all HTML elements in a much simpler avoiding + * leaving listeners behind which would result in memory leaks. + * + * @example + * Adding and removing event listeners + * ```javascript + * const eventListenerManager = new MultiTargetEventListenerManager() + * const element1 = document.getElementById('foo'); + * const element2 = document.getElementById('bar'); + * const mouseoverCallback = () => { }; + * const mouseoutCallback = () => { }; + * const dragCallback = () => { }; + * + * eventListenerManager.addEventListener(element1, 'mouseover', mouseoverCallback); + * eventListenerManager.addEventListener(element1, 'mouseout', mouseoutCallback); + * + * eventListenerManager.addEventListener(element2, 'voi.mousemove', dragCallback); + * eventListenerManager.addEventListener(element2, 'voi.drag', dragCallback); + * eventListenerManager.addEventListener(element2, 'voi.mouseup', () => { + * // do not need to store a reference of this function + * })); + * + * // Removes a specific event listener from element2 + * eventListenerManager.removeEventListener(element2, 'voi.mousemove', dragCallback) + * + * // Removes all "mouseup" event listeners added to "voi" namespace on element2 + * eventListenerManager.removeEventListener(element2, 'voi.mouseup') + * + * // Removes all event listeners added to element1 and element2 + * eventListenerManager.reset(); + * ``` + */ +class MultiTargetEventListenerManager { + private _targetsEventListeners = new Map(); + + public addEventListener( + target: EventTarget, + type: string, + callback: EventListener, + options?: AddEventListenerOptions + ) { + let eventListeners = this._targetsEventListeners.get(target); + + if (!eventListeners) { + eventListeners = new TargetEventListeners(target); + this._targetsEventListeners.set(target, eventListeners); + } + + eventListeners.addEventListener(type, callback, options); + } + + public removeEventListener( + target: EventTarget, + type: string, + callback?: EventListener, + options?: EventListenerOptions + ) { + const eventListeners = this._targetsEventListeners.get(target); + + if (!eventListeners) { + return; + } + + eventListeners.removeEventListener(type, callback, options); + + if (eventListeners.isEmpty) { + this._targetsEventListeners.delete(target); + } + } + + public reset() { + Array.from(this._targetsEventListeners.entries()).forEach( + ([target, targetEventListeners]) => { + targetEventListeners.reset(); + this._targetsEventListeners.delete(target); + } + ); + } +} + +export { + MultiTargetEventListenerManager as default, + MultiTargetEventListenerManager, +}; diff --git a/packages/core/src/ui/widgets/colorBar/EventListeners.ts b/packages/core/src/utilities/eventListener/TargetEventListeners.ts similarity index 73% rename from packages/core/src/ui/widgets/colorBar/EventListeners.ts rename to packages/core/src/utilities/eventListener/TargetEventListeners.ts index f6591eb027..9de6483209 100644 --- a/packages/core/src/ui/widgets/colorBar/EventListeners.ts +++ b/packages/core/src/utilities/eventListener/TargetEventListeners.ts @@ -6,6 +6,63 @@ enum EventListenerPhases { type ListenersMap = Map; +/** + * TargetEventListeners adds support for event types with namespace, allow + * removing events without having to pass a callback and makes it possible to + * remove all event listeners in a much simpler avoiding leaving listeners behind + * which would result in memory leaks. + * + * @example + * Creating a new TargetEventListeners instance + * ```javascript + * const element = document.getElementById('foo'); + * const targetEventListeners = new TargetEventListeners(element) + * ``` + * + * @example + * Adding and removing event listeners + * ```javascript + * const dragCallback = () => { }; + * + * targetEventListeners.addEventListener('voi.mousemove', dragCallback); + * targetEventListeners.addEventListener('voi.drag', dragCallback); + * targetEventListeners.addEventListener('voi.mouseup', () => { + * // do not need to store a reference of this function + * })); + * + * // Removes a specific event listener + * targetEventListeners.removeEventListener('voi.mousemove', dragCallback) + * + * // Removes all "mouseup" event listeners added to "colorbar.voi" namespace + * targetEventListeners.removeEventListener('voi.mouseup') + * + * // Removes all event listeners added to the element using this targetEventListeners + * // instance. A TargetEventListeners instance does not removes the event listeners + * // added by another one. + * targetEventListeners.reset(); + * ``` + * + * @example + * Adding and removing event listeners for capture and bubble phases. Each + * listener must be removed indenpendently + * ```javascript + * const clickCaptureCallback = () => { }; + * const clickBubbleCallback = () => { }; + * + * targetEventListeners.addEventListener('click', clickCaptureCallback, { capture: true }); + * targetEventListeners.addEventListener('click', clickBubbleCallback); + * + * // Removes the event listener added to the capture phase + * targetEventListeners.removeEventListener('click', clickCaptureCallback, { capture: true }); + * + * // Removes the event listener added to the bubble phase + * targetEventListeners.removeEventListener('click', clickBubbleCallback); + * + * // Removes all event listeners added to the HTML element + * targetEventListeners.reset(); + * ``` + + */ class TargetEventListeners { private _target: EventTarget; private _eventListeners = new Map(); @@ -79,12 +136,12 @@ class TargetEventListeners { } /** - * Loop through all types, listeners and phases removing all of them + * Loop through all types, listeners and phases and removing all of them */ - public dispose() { - // Dispose all children (DFS - depth first search) + public reset() { + // Destroy all children (DFS - depth first search) Array.from(this._children.entries()).forEach(([namespace, child]) => { - child.dispose(); + child.reset(); if (child.isEmpty) { this._children.delete(namespace); @@ -218,52 +275,4 @@ class TargetEventListeners { } } -class EventListenersManager { - private _targetsEventListeners = new Map(); - - public addEventListener( - target: EventTarget, - type: string, - callback: EventListener, - options?: AddEventListenerOptions - ) { - let eventListeners = this._targetsEventListeners.get(target); - - if (!eventListeners) { - eventListeners = new TargetEventListeners(target); - this._targetsEventListeners.set(target, eventListeners); - } - - eventListeners.addEventListener(type, callback, options); - } - - public removeEventListener( - target: EventTarget, - type: string, - callback?: EventListener, - options?: EventListenerOptions - ) { - const eventListeners = this._targetsEventListeners.get(target); - - if (!eventListeners) { - return; - } - - eventListeners.removeEventListener(type, callback, options); - - if (eventListeners.isEmpty) { - this._targetsEventListeners.delete(target); - } - } - - public dispose() { - Array.from(this._targetsEventListeners.entries()).forEach( - ([target, targetEventListeners]) => { - targetEventListeners.dispose(); - this._targetsEventListeners.delete(target); - } - ); - } -} - -export { TargetEventListeners, EventListenersManager }; +export { TargetEventListeners as default, TargetEventListeners }; diff --git a/packages/core/src/utilities/eventListener/index.ts b/packages/core/src/utilities/eventListener/index.ts new file mode 100644 index 0000000000..aed68efc6e --- /dev/null +++ b/packages/core/src/utilities/eventListener/index.ts @@ -0,0 +1,2 @@ +export { TargetEventListeners } from './TargetEventListeners'; +export { MultiTargetEventListenerManager } from './MultiTargetEventListenerManager'; diff --git a/packages/core/src/utilities/getVOIMultipliers.ts b/packages/core/src/utilities/getVOIMultipliers.ts new file mode 100644 index 0000000000..1d79d22bfb --- /dev/null +++ b/packages/core/src/utilities/getVOIMultipliers.ts @@ -0,0 +1,33 @@ +import * as utils from '.'; +import { IViewport } from '../types'; + +const DEFAULT_MULTIPLIER = 4; + +function getVOIMultipliers( + viewport: IViewport, + volumeId?: string, + options?: { + fixedPTWindowWidth?: boolean; + } +): [number, number] { + const modality = utils.getViewportModality(viewport, volumeId); + const isPreScaled = utils.isViewportPreScaled(viewport, volumeId); + const { fixedPTWindowWidth = true } = options ?? {}; + + if (modality === 'PT') { + const { clientWidth, clientHeight } = viewport.element; + const ptMultiplier = 5 / Math.max(clientWidth, clientHeight); + + // Set the "X" multiplier equal to zero in order to do not allow + // any change to the window width (0 * cursorDeltaX = 0) + const xMultiplier = fixedPTWindowWidth ? 0 : ptMultiplier; + + return isPreScaled + ? [xMultiplier, ptMultiplier] + : [xMultiplier, DEFAULT_MULTIPLIER]; + } + + return [DEFAULT_MULTIPLIER, DEFAULT_MULTIPLIER]; +} + +export { getVOIMultipliers as default, getVOIMultipliers }; diff --git a/packages/core/src/utilities/getViewportModality.ts b/packages/core/src/utilities/getViewportModality.ts new file mode 100644 index 0000000000..2f4b3f734c --- /dev/null +++ b/packages/core/src/utilities/getViewportModality.ts @@ -0,0 +1,23 @@ +import { IViewport } from '../types'; +import { StackViewport, VolumeViewport } from '../RenderingEngine'; +import cache from '../cache'; + +function getViewportModality(viewport: IViewport, volumeId?: string): string { + if (viewport instanceof StackViewport) { + return viewport.modality; + } + + if (viewport instanceof VolumeViewport) { + volumeId = volumeId ?? viewport.getDefaultActor()?.uid; + + if (!volumeId) { + return; + } + + return cache.getVolume(volumeId)?.metadata.Modality; + } + + throw new Error('Invalid viewport type'); +} + +export { getViewportModality as default, getViewportModality }; diff --git a/packages/core/src/utilities/index.ts b/packages/core/src/utilities/index.ts index 52c1a76dbd..1dd198ec03 100644 --- a/packages/core/src/utilities/index.ts +++ b/packages/core/src/utilities/index.ts @@ -1,3 +1,4 @@ +import * as eventListener from './eventListener'; import csUtils from './invertRgbTransferFunction'; import createSigmoidRGBTransferFunction from './createSigmoidRGBTransferFunction'; import getVoiFromSigmoidRGBTransferFunction from './getVoiFromSigmoidRGBTransferFunction'; @@ -10,11 +11,14 @@ import getRuntimeId from './getRuntimeId'; import imageIdToURI from './imageIdToURI'; import calibratedPixelSpacingMetadataProvider from './calibratedPixelSpacingMetadataProvider'; import isEqual from './isEqual'; +import isViewportPreScaled from './isViewportPreScaled'; import isOpposite from './isOpposite'; import createUint8SharedArray from './createUint8SharedArray'; import createFloat32SharedArray from './createFloat32SharedArray'; import createUint16SharedArray from './createUInt16SharedArray'; import createInt16SharedArray from './createInt16SharedArray'; +import getViewportModality from './getViewportModality'; +import getVOIMultipliers from './getVOIMultipliers'; import getClosestImageId from './getClosestImageId'; import getSpacingInNormalDirection from './getSpacingInNormalDirection'; import getTargetVolumeAndSpacingInNormalDir from './getTargetVolumeAndSpacingInNormalDir'; @@ -54,6 +58,7 @@ import * as colormap from './colormap'; import * as transferFunctionUtils from './transferFunctionUtils'; export { + eventListener, csUtils as invertRgbTransferFunction, createSigmoidRGBTransferFunction, getVoiFromSigmoidRGBTransferFunction, @@ -67,11 +72,14 @@ export { getMinMax, getRuntimeId, isEqual, + isViewportPreScaled, isOpposite, createFloat32SharedArray, createUint8SharedArray, createUint16SharedArray, createInt16SharedArray, + getViewportModality, + getVOIMultipliers, windowLevel, getClosestImageId, getSpacingInNormalDirection, diff --git a/packages/core/src/utilities/isViewportPreScaled.ts b/packages/core/src/utilities/isViewportPreScaled.ts new file mode 100644 index 0000000000..e3547177ea --- /dev/null +++ b/packages/core/src/utilities/isViewportPreScaled.ts @@ -0,0 +1,19 @@ +import { IViewport } from '../types'; +import { StackViewport, VolumeViewport } from '../RenderingEngine'; +import cache from '../cache'; + +function isViewportPreScaled(viewport: IViewport, volumeId?: string) { + if (viewport instanceof VolumeViewport) { + const volume = cache.getVolume(volumeId); + const { scaling } = volume; + + return !!scaling && Object.keys(scaling).length > 0; + } + + if (viewport instanceof StackViewport) { + const { preScale } = viewport.getImageData(); + return preScale.scaled && preScale.scalingParameters?.suvbw !== undefined; + } +} + +export { isViewportPreScaled as default, isViewportPreScaled }; diff --git a/utils/ExampleRunner/example-info.json b/utils/ExampleRunner/example-info.json index 8d3dd85bc8..5f741e724d 100644 --- a/utils/ExampleRunner/example-info.json +++ b/utils/ExampleRunner/example-info.json @@ -201,6 +201,10 @@ "dynamicPetCt": { "name": "Load a petCT data where PT series is 4D", "description": "Demonstrates how to render a 4D data into multiple viewports and fuse them" + }, + "colorBar": { + "name": "Color Bar", + "description": "Demonstrates how to add an interactive colobar to stack and volume viewports" } }, "tools-segmentation" : { From 9ba11b36257062b8e4f1cf9c6151ef248f09fcc9 Mon Sep 17 00:00:00 2001 From: Leonardo Campos Date: Tue, 24 Oct 2023 13:14:36 -0300 Subject: [PATCH 05/14] Fixeds interaction (mouse events + windowlevel tool) --- packages/core/examples/colorbar/index.ts | 1 - .../core/src/ui/widgets/colorBar/ColorBar.ts | 78 +++++++++++-------- .../src/ui/widgets/colorBar/ColorBarTicks.ts | 78 ++++++++++++++++++- .../ui/widgets/colorBar/ViewportColorBar.ts | 30 +++++++ 4 files changed, 151 insertions(+), 36 deletions(-) diff --git a/packages/core/examples/colorbar/index.ts b/packages/core/examples/colorbar/index.ts index cd5df4057f..52c08377c5 100644 --- a/packages/core/examples/colorbar/index.ts +++ b/packages/core/examples/colorbar/index.ts @@ -400,7 +400,6 @@ async function initializeViewport(renderingEngine, toolGroup, viewportInfo) { position: 'relative', width: '100%', height: '100%', - border: 'solid 1px #0f0', }); viewportGrid.appendChild(viewportContainer); diff --git a/packages/core/src/ui/widgets/colorBar/ColorBar.ts b/packages/core/src/ui/widgets/colorBar/ColorBar.ts index dcf18117ba..10da4e1e6f 100644 --- a/packages/core/src/ui/widgets/colorBar/ColorBar.ts +++ b/packages/core/src/ui/widgets/colorBar/ColorBar.ts @@ -26,8 +26,10 @@ class ColorBar extends Widget { private _activeColormapName: string; private _eventListenersManager: MultiTargetEventListenerManager; private _canvas: ColorBarCanvas; - private _rangeText: ColorBarTicks; + private _ticksBar: ColorBarTicks; private _rangeTextPosition: ColorBarRangeTextPosition; + + private _isMouseOver = false; private _isInteracting = false; constructor(props: ColorBarProps) { @@ -37,34 +39,16 @@ class ColorBar extends Widget { this._colormaps = ColorBar.getColormapsMap(props); this._activeColormapName = ColorBar.getInitialColormapName(props); this._canvas = this._createCanvas(props); - this._rangeText = this._createTicksBar(props); + this._ticksBar = this._createTicksBar(props); this._rangeTextPosition = props.rangeTextPosition ?? DEFAULTS.RANGE_TEXT_POSITION; this._canvas.appendTo(this.rootElement); - this._rangeText.appendTo(document.body); + this._ticksBar.appendTo(document.body); this._addRootElementEventListeners(); } - private static getColormapsMap(props: ColorBarProps) { - const { colormaps } = props; - - return colormaps.reduce( - (items, item) => items.set(item.Name, item), - new Map() - ); - } - - private static getInitialColormapName(props: ColorBarProps) { - const { activeColormapName, colormaps } = props; - const colormapExists = - !!activeColormapName && - colormaps.some((cm) => cm.Name === activeColormapName); - - return colormapExists ? activeColormapName : colormaps[0].Name; - } - /** * Returns the active LUT name */ @@ -97,7 +81,7 @@ class ColorBar extends Widget { public set imageRange(imageRange: ColorBarVOIRange) { this._canvas.imageRange = imageRange; - this._rangeText.imageRange = imageRange; + this._ticksBar.imageRange = imageRange; } public get voiRange() { @@ -115,7 +99,7 @@ class ColorBar extends Widget { } this._canvas.voiRange = voiRange; - this._rangeText.voiRange = voiRange; + this._ticksBar.voiRange = voiRange; this.onVoiChange(voiRange); } @@ -125,7 +109,7 @@ class ColorBar extends Widget { public set showFullImageRange(value: boolean) { this._canvas.showFullImageRange = value; - this._rangeText.showFullPixelValueRange = value; + this._ticksBar.showFullPixelValueRange = value; } public destroy() { @@ -157,6 +141,37 @@ class ColorBar extends Widget { // no-op } + protected showTicks() { + this.updateTicksBar(); + this._ticksBar.visible = true; + } + + protected hideTicks() { + if (this._isInteracting || this._isMouseOver) { + return; + } + + this._ticksBar.visible = false; + } + + private static getColormapsMap(props: ColorBarProps) { + const { colormaps } = props; + + return colormaps.reduce( + (items, item) => items.set(item.Name, item), + new Map() + ); + } + + private static getInitialColormapName(props: ColorBarProps) { + const { activeColormapName, colormaps } = props; + const colormapExists = + !!activeColormapName && + colormaps.some((cm) => cm.Name === activeColormapName); + + return colormapExists ? activeColormapName : colormaps[0].Name; + } + private _createCanvas(props: ColorBarProps) { const { imageRange, voiRange, showFullPixelValueRange } = props; const colormap = this._colormaps.get(this._activeColormapName); @@ -192,8 +207,8 @@ class ColorBar extends Widget { return { client: clientPoint, page: pagePoint, local: localPoints }; } - private showTicksBar() { - const { _rangeText: ticksBar } = this; + private updateTicksBar() { + const { _ticksBar: ticksBar } = this; const { width: containerWidth, height: containerHeight } = this.containerSize; const { top: containerTop, left: containerLeft } = @@ -225,23 +240,23 @@ class ColorBar extends Widget { ticksBar.top = ticksBarTop; ticksBar.left = ticksBarLeft; - ticksBar.visible = true; } private _mouseOverCallback = (evt) => { - this.showTicksBar(); + this._isMouseOver = true; + this.showTicks(); evt.stopPropagation(); }; private _mouseOutCallback = (evt) => { - if (!this._isInteracting) { - this._rangeText.visible = false; - } + this._isMouseOver = false; + this.hideTicks(); evt.stopPropagation(); }; private _mouseDownCallback = (evt: MouseEvent) => { this._isInteracting = true; + this.showTicks(); this._addVOIEventListeners(evt); evt.stopPropagation(); }; @@ -284,6 +299,7 @@ class ColorBar extends Widget { private _mouseUpCallback = (evt) => { this._isInteracting = false; + this.hideTicks(); this._removeVOIEventListeners(); evt.stopPropagation(); }; diff --git a/packages/core/src/ui/widgets/colorBar/ColorBarTicks.ts b/packages/core/src/ui/widgets/colorBar/ColorBarTicks.ts index 56195d0c4f..604568f151 100644 --- a/packages/core/src/ui/widgets/colorBar/ColorBarTicks.ts +++ b/packages/core/src/ui/widgets/colorBar/ColorBarTicks.ts @@ -86,10 +86,16 @@ class ColorBarTicks { this.render(); } + /** + * Canvas top position (pixels) + */ public get top(): number { return Number.parseInt(this._canvas.style.top); } + /** + * Change the canvas top position (pixels) + */ public set top(top: number) { const { _canvas: canvas } = this; const currentTop = this.top; @@ -102,10 +108,16 @@ class ColorBarTicks { this.render(); } + /** + * Canvas left position (pixels) + */ public get left(): number { return Number.parseInt(this._canvas.style.left); } + /** + * Change the canvas left position (pixels) + */ public set left(left: number) { const { _canvas: canvas } = this; const currentLeft = this.left; @@ -118,10 +130,16 @@ class ColorBarTicks { this.render(); } + /** + * Image range + */ public get imageRange() { return { ...this._imageRange }; } + /** + * Set the image range that should goes from minPixelValue to maxPixelValue + */ public set imageRange(imageRange: ColorBarVOIRange) { if ( !isRangeValid(imageRange) || @@ -134,10 +152,18 @@ class ColorBarTicks { this.render(); } + /** + * VOI range + * (lower: wc - ww / 2, upper: wc + ww / 2) + */ public get voiRange() { return { ...this._voiRange }; } + /** + * Set the VOI Range + * (lower: wc - ww / 2, upper: wc + ww / 2) + */ public set voiRange(voiRange: ColorBarVOIRange) { if ( !isRangeValid(voiRange) || @@ -150,10 +176,16 @@ class ColorBarTicks { this.render(); } + /** + * Tick size (pixels) + */ public get tickSize(): number { return this._tickSize; } + /** + * Set the tick size + */ public set tickSize(tickSize: number) { if (tickSize === this._tickSize) { return; @@ -163,10 +195,18 @@ class ColorBarTicks { this.render(); } + /** + * Tick width (pixels) + */ public get tickWidth(): number { return this._tickWidth; } + /** + * Set the tick width. This width is used as `lineWidth` by CanvasRenderingContext2D. + * + * https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/lineWidth + */ public set tickWidth(tickWidth: number) { if (tickWidth === this._tickWidth) { return; @@ -176,23 +216,43 @@ class ColorBarTicks { this.render(); } - public get tickColor(): string { + /** + * Color used for ticks and labels. + */ + public get color(): string { return this._color; } - public set tickColor(tickColor: string) { - if (tickColor === this._color) { + /** + * Set the color used for ticks and labels. This color is used as `strokeStyle` + * and `fillStyle` by CanvasRenderingContext2D. + * + * https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/fillStyle + * https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/strokeStyle + */ + public set color(color: string) { + if (color === this._color) { return; } - this._color = tickColor; + this._color = color; this.render(); } + /** + * Return `true` when the ticks displayed are in the range from `imageRange.lower` + * to `imageRange.upper` or `false` when they are in the range from `voiRange.lower` + * to `voiRange.upper` + */ public get showFullPixelValueRange(): boolean { return this._showFullPixelValueRange; } + /** + * Change which range should be used when rendering the ticks. Set it to `true` + * to show from `imageRange.lower` to `imageRange.upper` or `false` show from + * `voiRange.lower` to `voiRange.upper`. + */ public set showFullPixelValueRange(showFullRange: boolean) { if (showFullRange === this._showFullPixelValueRange) { return; @@ -202,10 +262,16 @@ class ColorBarTicks { this.render(); } + /** + * Ticks visibility + */ public get visible() { return this._canvas.style.display === 'block'; } + /** + * Show/Hide the ticks + */ public set visible(visible) { if (visible === this.visible) { return; @@ -218,6 +284,10 @@ class ColorBarTicks { } } + /** + * Append the canvas to its parent element + * @param container - HTML element where the canvas should be added to + */ public appendTo(container: HTMLElement) { container.appendChild(this._canvas); this.render(); diff --git a/packages/core/src/ui/widgets/colorBar/ViewportColorBar.ts b/packages/core/src/ui/widgets/colorBar/ViewportColorBar.ts index 5ad86434c6..c423cf6302 100644 --- a/packages/core/src/ui/widgets/colorBar/ViewportColorBar.ts +++ b/packages/core/src/ui/widgets/colorBar/ViewportColorBar.ts @@ -17,6 +17,9 @@ class ViewportColorBar extends ColorBar { private _element: HTMLDivElement; private _volumeId: string; + private _hideTicksTime: number; + private _hideTicksTimeoutId: number; + constructor(props: ViewportColorBarProps) { const { element, volumeId } = props; const imageRange = ViewportColorBar._getImageRange(element, volumeId); @@ -107,6 +110,32 @@ class ViewportColorBar extends ColorBar { : { lower: voiRange[0], upper: voiRange[1] }; } + private autoHideTicks = () => { + // Avoiding calling setTimeout multiple times when manipulating the VOI + // via WindowLevel tool for better performance + if (this._hideTicksTimeoutId) { + return; + } + + const timeLeft = this._hideTicksTime - Date.now(); + + if (timeLeft <= 0) { + this.hideTicks(); + } else { + this._hideTicksTimeoutId = window.setTimeout(() => { + // Recursive call until there is no more time left + this._hideTicksTimeoutId = 0; + this.autoHideTicks(); + }, timeLeft); + } + }; + + private showAndAutoHideTicks(interval = 1000) { + this._hideTicksTime = Date.now() + interval; + this.showTicks(); + this.autoHideTicks(); + } + private _stackNewImageCallback = () => { this.imageRange = ViewportColorBar._getImageRange(this._element); }; @@ -135,6 +164,7 @@ class ViewportColorBar extends ColorBar { } this.voiRange = voiRange; + this.showAndAutoHideTicks(); }; private _addCornerstoneEventListener() { From 5f223ce92790848da9255235d8c7b8e6181e0cf5 Mon Sep 17 00:00:00 2001 From: Leonardo Campos Date: Tue, 24 Oct 2023 23:01:10 -0300 Subject: [PATCH 06/14] code review --- packages/core/examples/colorbar/index.ts | 21 +++++++---- .../rendering/renderPseudoColorImage.ts | 5 +-- packages/core/src/ui/widgets/Widget.ts | 36 +++++++++++-------- .../core/src/ui/widgets/colorBar/ColorBar.ts | 34 ++++++++++++++---- .../src/ui/widgets/colorBar/ColorBarCanvas.ts | 20 ++++------- .../src/ui/widgets/colorBar/ColorBarTicks.ts | 11 +++--- .../src/ui/widgets/colorBar/ColormapList.ts | 5 --- .../ui/widgets/colorBar/ColormapListItem.ts | 11 ------ .../ui/widgets/colorBar/ViewportColorBar.ts | 2 +- .../common/isRangeTextPositionValid.ts | 16 +++++++++ .../enums/ColorBarRangeTextPosition.ts | 14 ++++---- .../colorBar/types/ColorBarCanvasProps.ts | 4 +-- .../colorBar/types/ColorBarCommonProps.ts | 11 +++--- .../colorBar/types/ColorBarTicksProps.ts | 3 +- .../colorBar/types/ColormapCanvasProps.ts | 14 -------- packages/core/src/utilities/clamp.ts | 5 +++ packages/core/src/utilities/index.ts | 4 +++ .../core/src/utilities/interpolateVec3.ts | 20 +++++++++++ utils/ExampleRunner/example-info.json | 2 +- 19 files changed, 140 insertions(+), 98 deletions(-) delete mode 100644 packages/core/src/ui/widgets/colorBar/ColormapList.ts delete mode 100644 packages/core/src/ui/widgets/colorBar/ColormapListItem.ts create mode 100644 packages/core/src/ui/widgets/colorBar/common/isRangeTextPositionValid.ts delete mode 100644 packages/core/src/ui/widgets/colorBar/types/ColormapCanvasProps.ts create mode 100644 packages/core/src/utilities/clamp.ts create mode 100644 packages/core/src/utilities/interpolateVec3.ts diff --git a/packages/core/examples/colorbar/index.ts b/packages/core/examples/colorbar/index.ts index 52c08377c5..cf4bcbd507 100644 --- a/packages/core/examples/colorbar/index.ts +++ b/packages/core/examples/colorbar/index.ts @@ -337,9 +337,12 @@ function initializeColorBarContainers(viewportInfo, viewportContainer) { // Create instaces of the color bars for CT or PT/CT viewports and add them to the DOM function initializeColorBars(viewportInfo, colorBarContainers) { - const { fusion, volumeIds = [], colorBar, viewportInput } = viewportInfo; + const { fusion, volumeIds, colorBar, viewportInput } = viewportInfo; const { element } = viewportInput; + // Stack viewports do not have volumeIds + const ctVolumeId = volumeIds?.length ? volumeIds[0] : undefined; + const scaleStyle = { font: '12px Arial', color: '#fff', @@ -353,16 +356,18 @@ function initializeColorBars(viewportInfo, colorBarContainers) { id: 'ctColorBar', element, container: colorBarContainers[0], - volumeId: volumeIds[0], + volumeId: ctVolumeId, colormaps, activeColormapName: 'Grayscale', - rangeTextPosition: ColorBarRangeTextPosition.TopOrLeft, - ticksStyle: scaleStyle, + ticks: { + position: ColorBarRangeTextPosition.Left, + style: scaleStyle, + }, }); colorBar.instances.push(ctColorBar); - if (fusion && volumeIds.length === 2) { + if (fusion && volumeIds?.length === 2) { const ptColorBar = new ViewportColorBar({ id: 'ptColorBar', element, @@ -370,8 +375,10 @@ function initializeColorBars(viewportInfo, colorBarContainers) { volumeId: volumeIds[1], colormaps, activeColormapName: currentPTColormapName, - rangeTextPosition: ColorBarRangeTextPosition.TopOrLeft, - ticksStyle: scaleStyle, + ticks: { + position: ColorBarRangeTextPosition.Left, + style: scaleStyle, + }, }); colorBar.instances.push(ptColorBar); diff --git a/packages/core/src/RenderingEngine/helpers/cpuFallback/rendering/renderPseudoColorImage.ts b/packages/core/src/RenderingEngine/helpers/cpuFallback/rendering/renderPseudoColorImage.ts index 41c24d071b..b4ff797f73 100644 --- a/packages/core/src/RenderingEngine/helpers/cpuFallback/rendering/renderPseudoColorImage.ts +++ b/packages/core/src/RenderingEngine/helpers/cpuFallback/rendering/renderPseudoColorImage.ts @@ -8,10 +8,7 @@ import storedPixelDataToCanvasImageDataPseudocolorLUT from './storedPixelDataToC import storedPixelDataToCanvasImageDataPseudocolorLUTPET from './storedPixelDataToCanvasImageDataPseudocolorLUTPET'; import * as colors from '../colors/index'; import type { IImage, CPUFallbackEnabledElement } from '../../../../types'; - -function clamp(value: number, min: number, max: number) { - return Math.max(min, Math.min(max, value)); -} +import { clamp } from '../../../../utilities'; /** * Returns an appropriate canvas to render the Image. If the canvas available in the cache is appropriate diff --git a/packages/core/src/ui/widgets/Widget.ts b/packages/core/src/ui/widgets/Widget.ts index 0a8d6e1bb6..4c4d4e8e21 100644 --- a/packages/core/src/ui/widgets/Widget.ts +++ b/packages/core/src/ui/widgets/Widget.ts @@ -67,24 +67,13 @@ abstract class Widget { return this._id; } + /** + * Widget's root element + */ public get rootElement(): HTMLElement { return this._rootElement; } - protected createRootElement(id: string): HTMLElement { - const rootElement = document.createElement('div'); - - rootElement.id = id; - rootElement.classList.add('widget'); - - Object.assign(rootElement.style, { - width: '100%', - height: '100%', - }); - - return rootElement; - } - /** * Append the widget to a parent element * @param container - HTML element where the widget should be added to @@ -127,6 +116,25 @@ abstract class Widget { return { ...this._containerSize }; } + /** + * Creates the root element which is a div by default + * @param id - Root element id + * @returns A new HTML element where all other elements should be added to + */ + protected createRootElement(id: string): HTMLElement { + const rootElement = document.createElement('div'); + + rootElement.id = id; + rootElement.classList.add('widget'); + + Object.assign(rootElement.style, { + width: '100%', + height: '100%', + }); + + return rootElement; + } + /** * Method called every time widget's container is resize giving the * opportunity to children classes to act when that happens. diff --git a/packages/core/src/ui/widgets/colorBar/ColorBar.ts b/packages/core/src/ui/widgets/colorBar/ColorBar.ts index 10da4e1e6f..7df9642ef9 100644 --- a/packages/core/src/ui/widgets/colorBar/ColorBar.ts +++ b/packages/core/src/ui/widgets/colorBar/ColorBar.ts @@ -6,12 +6,13 @@ import { isRangeValid, areColorBarRangesEqual } from './common'; import { ColorBarRangeTextPosition } from './enums/ColorBarRangeTextPosition'; import { ColorBarCanvas } from './ColorBarCanvas'; import ColorBarTicks from './ColorBarTicks'; +import isRangeTextPositionValid from './common/isRangeTextPositionValid'; const { MultiTargetEventListenerManager } = utilities.eventListener; const DEFAULTS = { MULTIPLIER: 1, - RANGE_TEXT_POSITION: ColorBarRangeTextPosition.BottomOrRight, + RANGE_TEXT_POSITION: ColorBarRangeTextPosition.Right, TICKS_BAR_SIZE: 50, }; @@ -41,7 +42,7 @@ class ColorBar extends Widget { this._canvas = this._createCanvas(props); this._ticksBar = this._createTicksBar(props); this._rangeTextPosition = - props.rangeTextPosition ?? DEFAULTS.RANGE_TEXT_POSITION; + props.ticks?.position ?? DEFAULTS.RANGE_TEXT_POSITION; this._canvas.appendTo(this.rootElement); this._ticksBar.appendTo(document.body); @@ -185,11 +186,12 @@ class ColorBar extends Widget { } public _createTicksBar(props: ColorBarProps): ColorBarTicks { + const ticksProps = props.ticks; + return new ColorBarTicks({ imageRange: props.imageRange, voiRange: props.voiRange, - ticksStyle: props.ticksStyle, - rangeTextPosition: props.rangeTextPosition, + ticks: ticksProps, showFullPixelValueRange: props.showFullPixelValueRange, }); } @@ -208,15 +210,33 @@ class ColorBar extends Widget { } private updateTicksBar() { - const { _ticksBar: ticksBar } = this; const { width: containerWidth, height: containerHeight } = this.containerSize; + + // ResizeObserver have not triggered any event when this happen + if (containerWidth === 0 && containerHeight === 0) { + return; + } + + const { _ticksBar: ticksBar, _rangeTextPosition: rangeTextPosition } = this; const { top: containerTop, left: containerLeft } = this.rootElement.getBoundingClientRect(); const isHorizontal = containerWidth >= containerHeight; const width = isHorizontal ? containerWidth : DEFAULTS.TICKS_BAR_SIZE; const height = isHorizontal ? DEFAULTS.TICKS_BAR_SIZE : containerHeight; + if ( + !isRangeTextPositionValid( + containerWidth, + containerHeight, + rangeTextPosition + ) + ) { + throw new Error( + 'Invalid rangeTextPosition value for the current colobar orientation' + ); + } + let ticksBarTop; let ticksBarLeft; @@ -224,7 +244,7 @@ class ColorBar extends Widget { if (isHorizontal) { ticksBarTop = - this._rangeTextPosition === ColorBarRangeTextPosition.TopOrLeft + rangeTextPosition === ColorBarRangeTextPosition.Top ? containerTop - height : containerTop + containerHeight; @@ -233,7 +253,7 @@ class ColorBar extends Widget { ticksBarTop = containerTop; ticksBarLeft = - this._rangeTextPosition === ColorBarRangeTextPosition.TopOrLeft + rangeTextPosition === ColorBarRangeTextPosition.Left ? containerLeft - width : containerLeft + containerWidth; } diff --git a/packages/core/src/ui/widgets/colorBar/ColorBarCanvas.ts b/packages/core/src/ui/widgets/colorBar/ColorBarCanvas.ts index bfaea2fbb0..cffd18d36a 100644 --- a/packages/core/src/ui/widgets/colorBar/ColorBarCanvas.ts +++ b/packages/core/src/ui/widgets/colorBar/ColorBarCanvas.ts @@ -1,6 +1,6 @@ -import { utilities } from '@cornerstonejs/core'; +import { Types, utilities } from '@cornerstonejs/core'; import { ColorBarCanvasProps } from './types/ColorBarCanvasProps'; -import type { ColorBarImageRange, ColorBarVOIRange, Colormap } from './types'; +import type { ColorBarImageRange, ColorBarVOIRange } from './types'; import type { ColorBarSize } from './types/ColorBarSize'; import { isRangeValid, @@ -9,15 +9,7 @@ import { areColorBarSizesEqual, } from './common'; -const clamp = (value, min, max) => Math.min(Math.max(min, value), max); - -const interpolateVec3 = (a, b, t) => { - return [ - a[0] * (1 - t) + b[0] * t, - a[1] * (1 - t) + b[1] * t, - a[2] * (1 - t) + b[2] * t, - ]; -}; +const { clamp, interpolateVec3 } = utilities; /** * Canvas referenced by the color bar where the colormap is rendered. It may @@ -27,7 +19,7 @@ class ColorBarCanvas { private _canvas: HTMLCanvasElement; private _imageRange: ColorBarImageRange; private _voiRange: ColorBarVOIRange; - private _colormap: Colormap; + private _colormap: Types.ColormapRegistration; private _showFullImageRange: boolean; constructor(props: ColorBarCanvasProps) { @@ -53,11 +45,11 @@ class ColorBarCanvas { } } - public get colormap(): Colormap { + public get colormap(): Types.ColormapRegistration { return this._colormap; } - public set colormap(colormap: Colormap) { + public set colormap(colormap: Types.ColormapRegistration) { this._colormap = colormap; this.render(); } diff --git a/packages/core/src/ui/widgets/colorBar/ColorBarTicks.ts b/packages/core/src/ui/widgets/colorBar/ColorBarTicks.ts index 604568f151..950ef6db85 100644 --- a/packages/core/src/ui/widgets/colorBar/ColorBarTicks.ts +++ b/packages/core/src/ui/widgets/colorBar/ColorBarTicks.ts @@ -46,12 +46,13 @@ class ColorBarTicks { size = { width: 20, height: 100 }, imageRange = { lower: 0, upper: 1 }, voiRange = { lower: 0, upper: 1 }, - ticksStyle, - rangeTextPosition, + ticks: ticksProps, container, showFullPixelValueRange = false, } = props; + const { style: ticksStyle, position: rangeTextPosition } = ticksProps ?? {}; + this._imageRange = imageRange; this._voiRange = voiRange; this._font = ticksStyle?.font ?? DEFAULTS.FONT; @@ -61,7 +62,7 @@ class ColorBarTicks { this._labelMargin = ticksStyle?.labelMargin ?? DEFAULTS.TICK_LABEL_MARGIN; this._maxNumTicks = ticksStyle?.maxNumTicks ?? DEFAULTS.MAX_NUM_TICKS; this._rangeTextPosition = - rangeTextPosition ?? ColorBarRangeTextPosition.TopOrLeft; + rangeTextPosition ?? ColorBarRangeTextPosition.Right; this._showFullPixelValueRange = showFullPixelValueRange; this._canvas = this._createCanvasElement(size, top, left); @@ -469,13 +470,13 @@ class ColorBarTicks { let tickInfo; if (isHorizontal) { - if (this._rangeTextPosition === ColorBarRangeTextPosition.TopOrLeft) { + if (this._rangeTextPosition === ColorBarRangeTextPosition.Top) { tickInfo = this._getTopTickInfo({ position, labelMeasure }); } else { tickInfo = this._getBottomTickInfo({ position, labelMeasure }); } } else { - if (this._rangeTextPosition === ColorBarRangeTextPosition.TopOrLeft) { + if (this._rangeTextPosition === ColorBarRangeTextPosition.Left) { tickInfo = this._getLeftTickInfo({ position, labelMeasure }); } else { tickInfo = this._getRightTickInfo({ position }); diff --git a/packages/core/src/ui/widgets/colorBar/ColormapList.ts b/packages/core/src/ui/widgets/colorBar/ColormapList.ts deleted file mode 100644 index 1fee41fecd..0000000000 --- a/packages/core/src/ui/widgets/colorBar/ColormapList.ts +++ /dev/null @@ -1,5 +0,0 @@ -class ColormapList { - // -} - -export { ColormapList as default, ColormapList }; diff --git a/packages/core/src/ui/widgets/colorBar/ColormapListItem.ts b/packages/core/src/ui/widgets/colorBar/ColormapListItem.ts deleted file mode 100644 index 973b344bdd..0000000000 --- a/packages/core/src/ui/widgets/colorBar/ColormapListItem.ts +++ /dev/null @@ -1,11 +0,0 @@ -import ColormapCanvas from './ColorBarCanvas'; - -class ColormapListItem { - private _canvas: ColormapCanvas; - - constructor() { - this._canvas = new ColormapCanvas(); - } -} - -export { ColormapListItem as default, ColormapListItem }; diff --git a/packages/core/src/ui/widgets/colorBar/ViewportColorBar.ts b/packages/core/src/ui/widgets/colorBar/ViewportColorBar.ts index c423cf6302..a5acd4d500 100644 --- a/packages/core/src/ui/widgets/colorBar/ViewportColorBar.ts +++ b/packages/core/src/ui/widgets/colorBar/ViewportColorBar.ts @@ -96,7 +96,7 @@ class ViewportColorBar extends ColorBar { ? viewport.getActor(volumeId) : viewport.getDefaultActor(); - if (!actor) { + if (!actor || !utilities.isImageActor(actor)) { return defaultImageRange; } diff --git a/packages/core/src/ui/widgets/colorBar/common/isRangeTextPositionValid.ts b/packages/core/src/ui/widgets/colorBar/common/isRangeTextPositionValid.ts new file mode 100644 index 0000000000..e36ff29709 --- /dev/null +++ b/packages/core/src/ui/widgets/colorBar/common/isRangeTextPositionValid.ts @@ -0,0 +1,16 @@ +import { ColorBarRangeTextPosition } from '../enums'; + +function isRangeTextPositionValid( + colorbarWidth: number, + colorbarHeight: number, + rangeTextPosition: ColorBarRangeTextPosition +) { + const isHorizontal = colorbarWidth >= colorbarHeight; + const validRangeTextPositions = isHorizontal + ? [ColorBarRangeTextPosition.Top, ColorBarRangeTextPosition.Bottom] + : [ColorBarRangeTextPosition.Left, ColorBarRangeTextPosition.Right]; + + return validRangeTextPositions.includes(rangeTextPosition); +} + +export { isRangeTextPositionValid as default, isRangeTextPositionValid }; diff --git a/packages/core/src/ui/widgets/colorBar/enums/ColorBarRangeTextPosition.ts b/packages/core/src/ui/widgets/colorBar/enums/ColorBarRangeTextPosition.ts index ee450e3b81..c4e72d2e7c 100644 --- a/packages/core/src/ui/widgets/colorBar/enums/ColorBarRangeTextPosition.ts +++ b/packages/core/src/ui/widgets/colorBar/enums/ColorBarRangeTextPosition.ts @@ -1,11 +1,11 @@ /** - * Specify the position of the text/ticks that are displayed when the user - * interacts with the color bar. The position of the text depends on the - * position of the color bar as follow: - * - TopOrLeft: horizontal/top OR vertical/left - * - BottomOrRight: horizontal/bottom OR vertical/right + * Specify the position of the text/ticks. + * Left/Right are the valid options for a vertical colorbars and Top/Bottom + * for the horizontal ones. */ export enum ColorBarRangeTextPosition { - TopOrLeft, - BottomOrRight, + Top = 'top', + Left = 'left', + Bottom = 'bottom', + Right = 'right', } diff --git a/packages/core/src/ui/widgets/colorBar/types/ColorBarCanvasProps.ts b/packages/core/src/ui/widgets/colorBar/types/ColorBarCanvasProps.ts index ebd5e94005..4ee38b7a1b 100644 --- a/packages/core/src/ui/widgets/colorBar/types/ColorBarCanvasProps.ts +++ b/packages/core/src/ui/widgets/colorBar/types/ColorBarCanvasProps.ts @@ -1,10 +1,10 @@ +import { Types } from '@cornerstonejs/core'; import type { ColorBarImageRange } from './ColorBarImageRange'; import type { ColorBarSize } from './ColorBarSize'; import type { ColorBarVOIRange } from './ColorBarVOIRange'; -import type { Colormap } from './Colormap'; export interface ColorBarCanvasProps { - colormap: Colormap; + colormap: Types.ColormapRegistration; size?: ColorBarSize; imageRange?: ColorBarImageRange; voiRange?: ColorBarVOIRange; diff --git a/packages/core/src/ui/widgets/colorBar/types/ColorBarCommonProps.ts b/packages/core/src/ui/widgets/colorBar/types/ColorBarCommonProps.ts index 2cb2765eaa..bf51b65ea6 100644 --- a/packages/core/src/ui/widgets/colorBar/types/ColorBarCommonProps.ts +++ b/packages/core/src/ui/widgets/colorBar/types/ColorBarCommonProps.ts @@ -10,10 +10,13 @@ export type ColorBarCommonProps = { imageRange?: ColorBarImageRange; // VOI Range that is related to Window Width and Window Center voiRange?: ColorBarVOIRange; - // Position where the range text should be displayed related to the ticks bar - rangeTextPosition?: ColorBarRangeTextPosition; - // Ticks style - ticksStyle?: ColorBarTicksStyle; + // Ticks props + ticks?: { + // Position where the range text (tiks) should be displayed related to the ticks bar + position?: ColorBarRangeTextPosition; + // Ticks style + style?: ColorBarTicksStyle; + }; // The color bar shall show a range from `imageRange.lower` to `imageRange.upper` // when it is set to `true` or from `voiRange.lower` to `voiRange.upper` otherwise. showFullPixelValueRange?: boolean; diff --git a/packages/core/src/ui/widgets/colorBar/types/ColorBarTicksProps.ts b/packages/core/src/ui/widgets/colorBar/types/ColorBarTicksProps.ts index d670fbde20..a829cac877 100644 --- a/packages/core/src/ui/widgets/colorBar/types/ColorBarTicksProps.ts +++ b/packages/core/src/ui/widgets/colorBar/types/ColorBarTicksProps.ts @@ -1,9 +1,8 @@ -import type { ColorBarCommonProps, ColorBarSize, ColorBarTicksStyle } from '.'; +import type { ColorBarCommonProps, ColorBarSize } from '.'; export type ColorBarTicksProps = ColorBarCommonProps & { top?: number; left?: number; size?: ColorBarSize; - ticksStyle?: ColorBarTicksStyle; container?: HTMLElement; }; diff --git a/packages/core/src/ui/widgets/colorBar/types/ColormapCanvasProps.ts b/packages/core/src/ui/widgets/colorBar/types/ColormapCanvasProps.ts deleted file mode 100644 index ebd5e94005..0000000000 --- a/packages/core/src/ui/widgets/colorBar/types/ColormapCanvasProps.ts +++ /dev/null @@ -1,14 +0,0 @@ -import type { ColorBarImageRange } from './ColorBarImageRange'; -import type { ColorBarSize } from './ColorBarSize'; -import type { ColorBarVOIRange } from './ColorBarVOIRange'; -import type { Colormap } from './Colormap'; - -export interface ColorBarCanvasProps { - colormap: Colormap; - size?: ColorBarSize; - imageRange?: ColorBarImageRange; - voiRange?: ColorBarVOIRange; - - container?: HTMLElement; - showFullPixelValueRange?: boolean; -} diff --git a/packages/core/src/utilities/clamp.ts b/packages/core/src/utilities/clamp.ts new file mode 100644 index 0000000000..697aa79d16 --- /dev/null +++ b/packages/core/src/utilities/clamp.ts @@ -0,0 +1,5 @@ +function clamp(value: number, min: number, max: number) { + return Math.max(min, Math.min(max, value)); +} + +export { clamp as default, clamp }; diff --git a/packages/core/src/utilities/index.ts b/packages/core/src/utilities/index.ts index 1dd198ec03..df97505e6f 100644 --- a/packages/core/src/utilities/index.ts +++ b/packages/core/src/utilities/index.ts @@ -10,6 +10,7 @@ import getMinMax from './getMinMax'; import getRuntimeId from './getRuntimeId'; import imageIdToURI from './imageIdToURI'; import calibratedPixelSpacingMetadataProvider from './calibratedPixelSpacingMetadataProvider'; +import clamp from './clamp'; import isEqual from './isEqual'; import isViewportPreScaled from './isViewportPreScaled'; import isOpposite from './isOpposite'; @@ -24,6 +25,7 @@ import getSpacingInNormalDirection from './getSpacingInNormalDirection'; import getTargetVolumeAndSpacingInNormalDir from './getTargetVolumeAndSpacingInNormalDir'; import getVolumeActorCorners from './getVolumeActorCorners'; import indexWithinDimensions from './indexWithinDimensions'; +import interpolateVec3 from './interpolateVec3'; import getVolumeViewportsContainingSameVolumes from './getVolumeViewportsContainingSameVolumes'; import getViewportsWithVolumeId from './getViewportsWithVolumeId'; import transformWorldToIndex from './transformWorldToIndex'; @@ -67,6 +69,7 @@ export { triggerEvent, imageIdToURI, calibratedPixelSpacingMetadataProvider, + clamp, uuidv4, planar, getMinMax, @@ -86,6 +89,7 @@ export { getTargetVolumeAndSpacingInNormalDir, getVolumeActorCorners, indexWithinDimensions, + interpolateVec3, getVolumeViewportsContainingSameVolumes, getViewportsWithVolumeId, transformWorldToIndex, diff --git a/packages/core/src/utilities/interpolateVec3.ts b/packages/core/src/utilities/interpolateVec3.ts new file mode 100644 index 0000000000..a8443eee1e --- /dev/null +++ b/packages/core/src/utilities/interpolateVec3.ts @@ -0,0 +1,20 @@ +/** + * Linear interpolation between two vec3. + * Can be used, for example, to interpolate between two RGB colors. + * @param a - First vec3 + * @param b - Second vec3 + * @param t - Time "t". + * Vector A is returned for values smaller than or equel to 0. + * Vector B is returned for values greater than or equal to 1. + * An interpolation between vector A and B is returned otherwise. + * @returns + */ +const interpolateVec3 = (a, b, t) => { + return [ + a[0] * (1 - t) + b[0] * t, + a[1] * (1 - t) + b[1] * t, + a[2] * (1 - t) + b[2] * t, + ]; +}; + +export { interpolateVec3 as default, interpolateVec3 }; diff --git a/utils/ExampleRunner/example-info.json b/utils/ExampleRunner/example-info.json index 5f741e724d..13994f68b0 100644 --- a/utils/ExampleRunner/example-info.json +++ b/utils/ExampleRunner/example-info.json @@ -203,7 +203,7 @@ "description": "Demonstrates how to render a 4D data into multiple viewports and fuse them" }, "colorBar": { - "name": "Color Bar", + "name": "Colorbar", "description": "Demonstrates how to add an interactive colobar to stack and volume viewports" } }, From cc293564e2b65eca105d27b646db9f93ba150360 Mon Sep 17 00:00:00 2001 From: Leonardo Campos Date: Tue, 24 Oct 2023 23:47:53 -0300 Subject: [PATCH 07/14] renamed [Cc]olorBar to [Cc]olorbar --- packages/core/examples/colorbar/index.ts | 78 +++++++++---------- .../colorBar/common/areColorBarRangesEqual.ts | 10 --- .../colorBar/common/areColorBarSizesEqual.ts | 7 -- .../src/ui/widgets/colorBar/common/index.ts | 4 - .../colorBar/common/isColorBarSizeValid.ts | 7 -- .../widgets/colorBar/common/isRangeValid.ts | 7 -- .../src/ui/widgets/colorBar/enums/index.ts | 1 - .../core/src/ui/widgets/colorBar/index.ts | 5 -- .../colorBar/types/ColorBarCanvasProps.ts | 14 ---- .../colorBar/types/ColorBarTicksProps.ts | 8 -- .../colorBar/types/ColorBarVOIRange.ts | 3 - .../colorBar/types/ViewportColorBarProps.ts | 6 -- .../src/ui/widgets/colorBar/types/index.ts | 8 -- .../ColorBar.ts => colorbar/Colorbar.ts} | 56 ++++++------- .../ColorbarCanvas.ts} | 50 ++++++------ .../ColorbarTicks.ts} | 60 +++++++------- .../ViewportColorbar.ts} | 20 ++--- .../colorbar/common/areColorbarRangesEqual.ts | 10 +++ .../colorbar/common/areColorbarSizesEqual.ts | 7 ++ .../src/ui/widgets/colorbar/common/index.ts | 4 + .../colorbar/common/isColorbarSizeValid.ts | 7 ++ .../common/isRangeTextPositionValid.ts | 8 +- .../widgets/colorbar/common/isRangeValid.ts | 7 ++ .../enums/ColorbarRangeTextPosition.ts} | 2 +- .../src/ui/widgets/colorbar/enums/index.ts | 1 + .../core/src/ui/widgets/colorbar/index.ts | 5 ++ .../colorbar/types/ColorbarCanvasProps.ts | 14 ++++ .../types/ColorbarCommonProps.ts} | 18 ++--- .../types/ColorbarImageRange.ts} | 2 +- .../types/ColorbarProps.ts} | 4 +- .../types/ColorbarSize.ts} | 2 +- .../colorbar/types/ColorbarTicksProps.ts | 8 ++ .../types/ColorbarTicksStyle.ts} | 2 +- .../colorbar/types/ColorbarVOIRange.ts | 3 + .../colorbar/types/ViewportColorbarProps.ts | 6 ++ .../src/ui/widgets/colorbar/types/index.ts | 8 ++ packages/core/src/ui/widgets/index.ts | 2 +- 37 files changed, 232 insertions(+), 232 deletions(-) delete mode 100644 packages/core/src/ui/widgets/colorBar/common/areColorBarRangesEqual.ts delete mode 100644 packages/core/src/ui/widgets/colorBar/common/areColorBarSizesEqual.ts delete mode 100644 packages/core/src/ui/widgets/colorBar/common/index.ts delete mode 100644 packages/core/src/ui/widgets/colorBar/common/isColorBarSizeValid.ts delete mode 100644 packages/core/src/ui/widgets/colorBar/common/isRangeValid.ts delete mode 100644 packages/core/src/ui/widgets/colorBar/enums/index.ts delete mode 100644 packages/core/src/ui/widgets/colorBar/index.ts delete mode 100644 packages/core/src/ui/widgets/colorBar/types/ColorBarCanvasProps.ts delete mode 100644 packages/core/src/ui/widgets/colorBar/types/ColorBarTicksProps.ts delete mode 100644 packages/core/src/ui/widgets/colorBar/types/ColorBarVOIRange.ts delete mode 100644 packages/core/src/ui/widgets/colorBar/types/ViewportColorBarProps.ts delete mode 100644 packages/core/src/ui/widgets/colorBar/types/index.ts rename packages/core/src/ui/widgets/{colorBar/ColorBar.ts => colorbar/Colorbar.ts} (85%) rename packages/core/src/ui/widgets/{colorBar/ColorBarCanvas.ts => colorbar/ColorbarCanvas.ts} (85%) rename packages/core/src/ui/widgets/{colorBar/ColorBarTicks.ts => colorbar/ColorbarTicks.ts} (89%) rename packages/core/src/ui/widgets/{colorBar/ViewportColorBar.ts => colorbar/ViewportColorbar.ts} (88%) create mode 100644 packages/core/src/ui/widgets/colorbar/common/areColorbarRangesEqual.ts create mode 100644 packages/core/src/ui/widgets/colorbar/common/areColorbarSizesEqual.ts create mode 100644 packages/core/src/ui/widgets/colorbar/common/index.ts create mode 100644 packages/core/src/ui/widgets/colorbar/common/isColorbarSizeValid.ts rename packages/core/src/ui/widgets/{colorBar => colorbar}/common/isRangeTextPositionValid.ts (60%) create mode 100644 packages/core/src/ui/widgets/colorbar/common/isRangeValid.ts rename packages/core/src/ui/widgets/{colorBar/enums/ColorBarRangeTextPosition.ts => colorbar/enums/ColorbarRangeTextPosition.ts} (85%) create mode 100644 packages/core/src/ui/widgets/colorbar/enums/index.ts create mode 100644 packages/core/src/ui/widgets/colorbar/index.ts create mode 100644 packages/core/src/ui/widgets/colorbar/types/ColorbarCanvasProps.ts rename packages/core/src/ui/widgets/{colorBar/types/ColorBarCommonProps.ts => colorbar/types/ColorbarCommonProps.ts} (63%) rename packages/core/src/ui/widgets/{colorBar/types/ColorBarImageRange.ts => colorbar/types/ColorbarImageRange.ts} (51%) rename packages/core/src/ui/widgets/{colorBar/types/ColorBarProps.ts => colorbar/types/ColorbarProps.ts} (62%) rename packages/core/src/ui/widgets/{colorBar/types/ColorBarSize.ts => colorbar/types/ColorbarSize.ts} (56%) create mode 100644 packages/core/src/ui/widgets/colorbar/types/ColorbarTicksProps.ts rename packages/core/src/ui/widgets/{colorBar/types/ColorBarTicksStyle.ts => colorbar/types/ColorbarTicksStyle.ts} (78%) create mode 100644 packages/core/src/ui/widgets/colorbar/types/ColorbarVOIRange.ts create mode 100644 packages/core/src/ui/widgets/colorbar/types/ViewportColorbarProps.ts create mode 100644 packages/core/src/ui/widgets/colorbar/types/index.ts diff --git a/packages/core/examples/colorbar/index.ts b/packages/core/examples/colorbar/index.ts index cf4bcbd507..18c2f09d27 100644 --- a/packages/core/examples/colorbar/index.ts +++ b/packages/core/examples/colorbar/index.ts @@ -18,8 +18,8 @@ import { } from '../../../../utils/demo/helpers'; import * as cornerstoneTools from '@cornerstonejs/tools'; -const { ViewportColorBar } = ui.widgets.colorbar; -const { ColorBarRangeTextPosition } = ui.widgets.colorbar.Enums; +const { ViewportColorbar } = ui.widgets.colorbar; +const { ColorbarRangeTextPosition } = ui.widgets.colorbar.Enums; // This is for debugging purposes console.warn( @@ -39,7 +39,7 @@ const { ViewportType } = Enums; const { MouseBindings } = csToolsEnums; const renderingEngineId = 'myRenderingEngine'; const toolGroupIds = new Set(); -const colorBarWidth = 20; // px +const colorbarWidth = 20; // px const imageIdsCache = new Map(); const wadoRsRoot = 'https://d3t6nz73ql33tx.cloudfront.net/dicomweb'; @@ -72,7 +72,7 @@ const viewportsInfo = [ { toolGroupId: 'STACK_TOOLGROUP_ID', fusion: false, - colorBar: { + colorbar: { position: 'right', instances: [], }, @@ -89,7 +89,7 @@ const viewportsInfo = [ volumeIds: [ctVolumeId, ptVolumeId], toolGroupId: 'VOLUME_TOOLGROUP_ID', fusion: true, - colorBar: { + colorbar: { position: 'right', instances: [], }, @@ -107,7 +107,7 @@ const viewportsInfo = [ volumeIds: [ctVolumeId, ptVolumeId], toolGroupId: 'VOLUME_TOOLGROUP_ID', fusion: true, - colorBar: { + colorbar: { position: 'right', instances: [], }, @@ -183,17 +183,17 @@ addDropdownToToolbar({ // Change the colormap of an specific viewport function setPTViewportColormap(viewportInfo, colormapName: string) { - const { fusion, colorBar, viewportInput } = viewportInfo; + const { fusion, colorbar, viewportInput } = viewportInfo; const { viewportId } = viewportInput; if (!fusion) { return; } - const ptColorBar = colorBar?.instances?.[1]; + const ptColorbar = colorbar?.instances?.[1]; - if (ptColorBar) { - ptColorBar.activeColormapName = colormapName; + if (ptColorbar) { + ptColorbar.activeColormapName = colormapName; } // Get the rendering engine @@ -267,7 +267,7 @@ async function initializeVolumeViewport( } // Creates one or more containers at the right side of the viewport -function createRightColorBarContainers(numContainers) { +function createRightColorbarContainers(numContainers) { const containers = []; const height = 100 / numContainers; let top = 0; @@ -278,8 +278,8 @@ function createRightColorBarContainers(numContainers) { Object.assign(container.style, { position: 'absolute', top: `${top}%`, - left: `calc(100% - ${colorBarWidth}px)`, - width: `${colorBarWidth}px`, + left: `calc(100% - ${colorbarWidth}px)`, + width: `${colorbarWidth}px`, height: `${100 / numContainers}%`, }); @@ -290,7 +290,7 @@ function createRightColorBarContainers(numContainers) { } // Creates one or more containers at the bottom of the viewport -function createBottomColorBarContainers(numContainers) { +function createBottomColorbarContainers(numContainers) { const containers = []; const width = 100 / numContainers; let left = 0; @@ -300,10 +300,10 @@ function createBottomColorBarContainers(numContainers) { Object.assign(container.style, { position: 'absolute', - top: `calc(100% - ${colorBarWidth}px)`, + top: `calc(100% - ${colorbarWidth}px)`, left: `${left}%`, width: `${width}%`, - height: `${colorBarWidth}px`, + height: `${colorbarWidth}px`, }); containers.push(container); @@ -313,13 +313,13 @@ function createBottomColorBarContainers(numContainers) { } // Creates one or more containers at the right side or at the bottom -// of the viewport based on `colorBar.position` config -function initializeColorBarContainers(viewportInfo, viewportContainer) { +// of the viewport based on `colorbar.position` config +function initializeColorbarContainers(viewportInfo, viewportContainer) { const numContainers = viewportInfo.fusion ? 2 : 1; const containers = - viewportInfo.colorBar?.position === 'right' - ? createRightColorBarContainers(numContainers) - : createBottomColorBarContainers(numContainers); + viewportInfo.colorbar?.position === 'right' + ? createRightColorbarContainers(numContainers) + : createBottomColorbarContainers(numContainers); containers.forEach((container) => { Object.assign(container.style, { @@ -336,8 +336,8 @@ function initializeColorBarContainers(viewportInfo, viewportContainer) { } // Create instaces of the color bars for CT or PT/CT viewports and add them to the DOM -function initializeColorBars(viewportInfo, colorBarContainers) { - const { fusion, volumeIds, colorBar, viewportInput } = viewportInfo; +function initializeColorbars(viewportInfo, colorbarContainers) { + const { fusion, volumeIds, colorbar, viewportInput } = viewportInfo; const { element } = viewportInput; // Stack viewports do not have volumeIds @@ -352,36 +352,36 @@ function initializeColorBars(viewportInfo, colorBarContainers) { labelMargin: 3, }; - const ctColorBar = new ViewportColorBar({ - id: 'ctColorBar', + const ctColorbar = new ViewportColorbar({ + id: 'ctColorbar', element, - container: colorBarContainers[0], + container: colorbarContainers[0], volumeId: ctVolumeId, colormaps, activeColormapName: 'Grayscale', ticks: { - position: ColorBarRangeTextPosition.Left, + position: ColorbarRangeTextPosition.Left, style: scaleStyle, }, }); - colorBar.instances.push(ctColorBar); + colorbar.instances.push(ctColorbar); if (fusion && volumeIds?.length === 2) { - const ptColorBar = new ViewportColorBar({ - id: 'ptColorBar', + const ptColorbar = new ViewportColorbar({ + id: 'ptColorbar', element, - container: colorBarContainers[1], + container: colorbarContainers[1], volumeId: volumeIds[1], colormaps, activeColormapName: currentPTColormapName, ticks: { - position: ColorBarRangeTextPosition.Left, + position: ColorbarRangeTextPosition.Left, style: scaleStyle, }, }); - colorBar.instances.push(ptColorBar); + colorbar.instances.push(ptColorbar); } } @@ -417,10 +417,10 @@ async function initializeViewport(renderingEngine, toolGroup, viewportInfo) { // Leave some space for the color bar that can be added to the // left or at the bottom of the viewport - if (viewportInfo.colorBar?.position === 'right') { - width = `calc(100% - ${colorBarWidth}px)`; + if (viewportInfo.colorbar?.position === 'right') { + width = `calc(100% - ${colorbarWidth}px)`; } else { - height = `calc(100% - ${colorBarWidth}px)`; + height = `calc(100% - ${colorbarWidth}px)`; } // Disable right click context menu so we can have right click tools @@ -442,14 +442,14 @@ async function initializeViewport(renderingEngine, toolGroup, viewportInfo) { toolGroup.addViewport(viewportId, renderingEngineId); // Create the color bar containers that will be used to append the - // colorBars' rootElement - const colorBarContainers = initializeColorBarContainers( + // colorbars' rootElement + const colorbarContainers = initializeColorbarContainers( viewportInfo, viewportContainer ); // Create and add the color bars to the DOM - initializeColorBars(viewportInfo, colorBarContainers); + initializeColorbars(viewportInfo, colorbarContainers); const ctImageIds = await getCTImageIds(); const viewport = renderingEngine.getViewport(viewportId); diff --git a/packages/core/src/ui/widgets/colorBar/common/areColorBarRangesEqual.ts b/packages/core/src/ui/widgets/colorBar/common/areColorBarRangesEqual.ts deleted file mode 100644 index 2c6250e2cf..0000000000 --- a/packages/core/src/ui/widgets/colorBar/common/areColorBarRangesEqual.ts +++ /dev/null @@ -1,10 +0,0 @@ -import type { ColorBarImageRange } from '../types/ColorBarImageRange'; - -const areColorBarRangesEqual = ( - a: ColorBarImageRange, - b: ColorBarImageRange -) => { - return !!a && !!b && a.lower === b.lower && a.upper === b.upper; -}; - -export { areColorBarRangesEqual as default, areColorBarRangesEqual }; diff --git a/packages/core/src/ui/widgets/colorBar/common/areColorBarSizesEqual.ts b/packages/core/src/ui/widgets/colorBar/common/areColorBarSizesEqual.ts deleted file mode 100644 index da68bb816c..0000000000 --- a/packages/core/src/ui/widgets/colorBar/common/areColorBarSizesEqual.ts +++ /dev/null @@ -1,7 +0,0 @@ -import type { ColorBarSize } from '../types/ColorBarSize'; - -const areColorBarSizesEqual = (a: ColorBarSize, b: ColorBarSize) => { - return !!a && !!b && a.width === b.width && a.height === b.height; -}; - -export { areColorBarSizesEqual as default, areColorBarSizesEqual }; diff --git a/packages/core/src/ui/widgets/colorBar/common/index.ts b/packages/core/src/ui/widgets/colorBar/common/index.ts deleted file mode 100644 index a85bfd6122..0000000000 --- a/packages/core/src/ui/widgets/colorBar/common/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -export { isRangeValid } from './isRangeValid'; -export { isColorBarSizeValid } from './isColorBarSizeValid'; -export { areColorBarRangesEqual } from './areColorBarRangesEqual'; -export { areColorBarSizesEqual } from './areColorBarSizesEqual'; diff --git a/packages/core/src/ui/widgets/colorBar/common/isColorBarSizeValid.ts b/packages/core/src/ui/widgets/colorBar/common/isColorBarSizeValid.ts deleted file mode 100644 index 01ffe359c2..0000000000 --- a/packages/core/src/ui/widgets/colorBar/common/isColorBarSizeValid.ts +++ /dev/null @@ -1,7 +0,0 @@ -import type { ColorBarSize } from '../types/ColorBarSize'; - -const isColorBarSizeValid = (size: ColorBarSize) => { - return !!size && size.width > 0 && size.height > 0; -}; - -export { isColorBarSizeValid as default, isColorBarSizeValid }; diff --git a/packages/core/src/ui/widgets/colorBar/common/isRangeValid.ts b/packages/core/src/ui/widgets/colorBar/common/isRangeValid.ts deleted file mode 100644 index 71a0a505c9..0000000000 --- a/packages/core/src/ui/widgets/colorBar/common/isRangeValid.ts +++ /dev/null @@ -1,7 +0,0 @@ -import type { ColorBarImageRange } from '../types/ColorBarImageRange'; - -const isRangeValid = (range: ColorBarImageRange) => { - return range && range.upper > range.lower; -}; - -export { isRangeValid as default, isRangeValid }; diff --git a/packages/core/src/ui/widgets/colorBar/enums/index.ts b/packages/core/src/ui/widgets/colorBar/enums/index.ts deleted file mode 100644 index 6ebc46ba34..0000000000 --- a/packages/core/src/ui/widgets/colorBar/enums/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { ColorBarRangeTextPosition } from './ColorBarRangeTextPosition'; diff --git a/packages/core/src/ui/widgets/colorBar/index.ts b/packages/core/src/ui/widgets/colorBar/index.ts deleted file mode 100644 index aff71cd30a..0000000000 --- a/packages/core/src/ui/widgets/colorBar/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -export * as Enums from './enums'; -export type * as Types from './types'; - -export { ColorBar } from './ColorBar'; -export { ViewportColorBar } from './ViewportColorBar'; diff --git a/packages/core/src/ui/widgets/colorBar/types/ColorBarCanvasProps.ts b/packages/core/src/ui/widgets/colorBar/types/ColorBarCanvasProps.ts deleted file mode 100644 index 4ee38b7a1b..0000000000 --- a/packages/core/src/ui/widgets/colorBar/types/ColorBarCanvasProps.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { Types } from '@cornerstonejs/core'; -import type { ColorBarImageRange } from './ColorBarImageRange'; -import type { ColorBarSize } from './ColorBarSize'; -import type { ColorBarVOIRange } from './ColorBarVOIRange'; - -export interface ColorBarCanvasProps { - colormap: Types.ColormapRegistration; - size?: ColorBarSize; - imageRange?: ColorBarImageRange; - voiRange?: ColorBarVOIRange; - - container?: HTMLElement; - showFullPixelValueRange?: boolean; -} diff --git a/packages/core/src/ui/widgets/colorBar/types/ColorBarTicksProps.ts b/packages/core/src/ui/widgets/colorBar/types/ColorBarTicksProps.ts deleted file mode 100644 index a829cac877..0000000000 --- a/packages/core/src/ui/widgets/colorBar/types/ColorBarTicksProps.ts +++ /dev/null @@ -1,8 +0,0 @@ -import type { ColorBarCommonProps, ColorBarSize } from '.'; - -export type ColorBarTicksProps = ColorBarCommonProps & { - top?: number; - left?: number; - size?: ColorBarSize; - container?: HTMLElement; -}; diff --git a/packages/core/src/ui/widgets/colorBar/types/ColorBarVOIRange.ts b/packages/core/src/ui/widgets/colorBar/types/ColorBarVOIRange.ts deleted file mode 100644 index 2eafbb62c0..0000000000 --- a/packages/core/src/ui/widgets/colorBar/types/ColorBarVOIRange.ts +++ /dev/null @@ -1,3 +0,0 @@ -import type { ColorBarImageRange } from './ColorBarImageRange'; - -export type ColorBarVOIRange = ColorBarImageRange; diff --git a/packages/core/src/ui/widgets/colorBar/types/ViewportColorBarProps.ts b/packages/core/src/ui/widgets/colorBar/types/ViewportColorBarProps.ts deleted file mode 100644 index ab7f590322..0000000000 --- a/packages/core/src/ui/widgets/colorBar/types/ViewportColorBarProps.ts +++ /dev/null @@ -1,6 +0,0 @@ -import type { ColorBarProps } from './ColorBarProps'; - -export type ViewportColorBarProps = ColorBarProps & { - element: HTMLDivElement; - volumeId?: string; -}; diff --git a/packages/core/src/ui/widgets/colorBar/types/index.ts b/packages/core/src/ui/widgets/colorBar/types/index.ts deleted file mode 100644 index 31f0cdcac9..0000000000 --- a/packages/core/src/ui/widgets/colorBar/types/index.ts +++ /dev/null @@ -1,8 +0,0 @@ -export type { ColorBarCommonProps } from './ColorBarCommonProps'; -export type { ColorBarProps } from './ColorBarProps'; -export type { ColorBarImageRange } from './ColorBarImageRange'; -export type { ColorBarVOIRange } from './ColorBarVOIRange'; -export type { ColorBarSize } from './ColorBarSize'; -export type { ColorBarTicksProps } from './ColorBarTicksProps'; -export type { ColorBarTicksStyle } from './ColorBarTicksStyle'; -export type { ViewportColorBarProps } from './ViewportColorBarProps'; diff --git a/packages/core/src/ui/widgets/colorBar/ColorBar.ts b/packages/core/src/ui/widgets/colorbar/Colorbar.ts similarity index 85% rename from packages/core/src/ui/widgets/colorBar/ColorBar.ts rename to packages/core/src/ui/widgets/colorbar/Colorbar.ts index 7df9642ef9..1edd13c268 100644 --- a/packages/core/src/ui/widgets/colorBar/ColorBar.ts +++ b/packages/core/src/ui/widgets/colorbar/Colorbar.ts @@ -1,44 +1,44 @@ import { vec2 } from 'gl-matrix'; import { utilities, Types } from '@cornerstonejs/core'; import { Widget } from '../Widget'; -import type { ColorBarProps, ColorBarVOIRange } from './types'; -import { isRangeValid, areColorBarRangesEqual } from './common'; -import { ColorBarRangeTextPosition } from './enums/ColorBarRangeTextPosition'; -import { ColorBarCanvas } from './ColorBarCanvas'; -import ColorBarTicks from './ColorBarTicks'; +import type { ColorbarProps, ColorbarVOIRange } from './types'; +import { isRangeValid, areColorbarRangesEqual } from './common'; +import { ColorbarRangeTextPosition } from './enums/ColorbarRangeTextPosition'; +import { ColorbarCanvas } from './ColorbarCanvas'; +import { ColorbarTicks } from './ColorbarTicks'; import isRangeTextPositionValid from './common/isRangeTextPositionValid'; const { MultiTargetEventListenerManager } = utilities.eventListener; const DEFAULTS = { MULTIPLIER: 1, - RANGE_TEXT_POSITION: ColorBarRangeTextPosition.Right, + RANGE_TEXT_POSITION: ColorbarRangeTextPosition.Right, TICKS_BAR_SIZE: 50, }; -type ColorBarPoints = { +type ColorbarPoints = { page: Types.Point2; client: Types.Point2; local: Types.Point2; }; -class ColorBar extends Widget { +class Colorbar extends Widget { private _colormaps: Map; private _activeColormapName: string; private _eventListenersManager: MultiTargetEventListenerManager; - private _canvas: ColorBarCanvas; - private _ticksBar: ColorBarTicks; - private _rangeTextPosition: ColorBarRangeTextPosition; + private _canvas: ColorbarCanvas; + private _ticksBar: ColorbarTicks; + private _rangeTextPosition: ColorbarRangeTextPosition; private _isMouseOver = false; private _isInteracting = false; - constructor(props: ColorBarProps) { + constructor(props: ColorbarProps) { super(props); this._eventListenersManager = new MultiTargetEventListenerManager(); - this._colormaps = ColorBar.getColormapsMap(props); - this._activeColormapName = ColorBar.getInitialColormapName(props); + this._colormaps = Colorbar.getColormapsMap(props); + this._activeColormapName = Colorbar.getInitialColormapName(props); this._canvas = this._createCanvas(props); this._ticksBar = this._createTicksBar(props); this._rangeTextPosition = @@ -80,7 +80,7 @@ class ColorBar extends Widget { return this._canvas.imageRange; } - public set imageRange(imageRange: ColorBarVOIRange) { + public set imageRange(imageRange: ColorbarVOIRange) { this._canvas.imageRange = imageRange; this._ticksBar.imageRange = imageRange; } @@ -89,12 +89,12 @@ class ColorBar extends Widget { return this._canvas.voiRange; } - public set voiRange(voiRange: ColorBarVOIRange) { + public set voiRange(voiRange: ColorbarVOIRange) { const { voiRange: currentVoiRange } = this._canvas; if ( !isRangeValid(voiRange) || - areColorBarRangesEqual(voiRange, currentVoiRange) + areColorbarRangesEqual(voiRange, currentVoiRange) ) { return; } @@ -138,7 +138,7 @@ class ColorBar extends Widget { return [DEFAULTS.MULTIPLIER, DEFAULTS.MULTIPLIER]; } - protected onVoiChange(voiRange: ColorBarVOIRange) { + protected onVoiChange(voiRange: ColorbarVOIRange) { // no-op } @@ -155,7 +155,7 @@ class ColorBar extends Widget { this._ticksBar.visible = false; } - private static getColormapsMap(props: ColorBarProps) { + private static getColormapsMap(props: ColorbarProps) { const { colormaps } = props; return colormaps.reduce( @@ -164,7 +164,7 @@ class ColorBar extends Widget { ); } - private static getInitialColormapName(props: ColorBarProps) { + private static getInitialColormapName(props: ColorbarProps) { const { activeColormapName, colormaps } = props; const colormapExists = !!activeColormapName && @@ -173,11 +173,11 @@ class ColorBar extends Widget { return colormapExists ? activeColormapName : colormaps[0].Name; } - private _createCanvas(props: ColorBarProps) { + private _createCanvas(props: ColorbarProps) { const { imageRange, voiRange, showFullPixelValueRange } = props; const colormap = this._colormaps.get(this._activeColormapName); - return new ColorBarCanvas({ + return new ColorbarCanvas({ colormap, imageRange, voiRange: voiRange, @@ -185,10 +185,10 @@ class ColorBar extends Widget { }); } - public _createTicksBar(props: ColorBarProps): ColorBarTicks { + public _createTicksBar(props: ColorbarProps): ColorbarTicks { const ticksProps = props.ticks; - return new ColorBarTicks({ + return new ColorbarTicks({ imageRange: props.imageRange, voiRange: props.voiRange, ticks: ticksProps, @@ -196,7 +196,7 @@ class ColorBar extends Widget { }); } - private _getPointsFromMouseEvent(evt: MouseEvent): ColorBarPoints { + private _getPointsFromMouseEvent(evt: MouseEvent): ColorbarPoints { const { rootElement: element } = this; const clientPoint: Types.Point2 = [evt.clientX, evt.clientY]; const pagePoint: Types.Point2 = [evt.pageX, evt.pageY]; @@ -244,7 +244,7 @@ class ColorBar extends Widget { if (isHorizontal) { ticksBarTop = - rangeTextPosition === ColorBarRangeTextPosition.Top + rangeTextPosition === ColorbarRangeTextPosition.Top ? containerTop - height : containerTop + containerHeight; @@ -253,7 +253,7 @@ class ColorBar extends Widget { ticksBarTop = containerTop; ticksBarLeft = - rangeTextPosition === ColorBarRangeTextPosition.Left + rangeTextPosition === ColorbarRangeTextPosition.Left ? containerLeft - width : containerLeft + containerWidth; } @@ -355,4 +355,4 @@ class ColorBar extends Widget { } } -export { ColorBar as default, ColorBar }; +export { Colorbar as default, Colorbar }; diff --git a/packages/core/src/ui/widgets/colorBar/ColorBarCanvas.ts b/packages/core/src/ui/widgets/colorbar/ColorbarCanvas.ts similarity index 85% rename from packages/core/src/ui/widgets/colorBar/ColorBarCanvas.ts rename to packages/core/src/ui/widgets/colorbar/ColorbarCanvas.ts index cffd18d36a..9239fefd7d 100644 --- a/packages/core/src/ui/widgets/colorBar/ColorBarCanvas.ts +++ b/packages/core/src/ui/widgets/colorbar/ColorbarCanvas.ts @@ -1,12 +1,12 @@ import { Types, utilities } from '@cornerstonejs/core'; -import { ColorBarCanvasProps } from './types/ColorBarCanvasProps'; -import type { ColorBarImageRange, ColorBarVOIRange } from './types'; -import type { ColorBarSize } from './types/ColorBarSize'; +import { ColorbarCanvasProps } from './types/ColorbarCanvasProps'; +import type { ColorbarImageRange, ColorbarVOIRange } from './types'; +import type { ColorbarSize } from './types/ColorbarSize'; import { isRangeValid, - areColorBarRangesEqual, - isColorBarSizeValid, - areColorBarSizesEqual, + areColorbarRangesEqual, + isColorbarSizeValid, + areColorbarSizesEqual, } from './common'; const { clamp, interpolateVec3 } = utilities; @@ -15,15 +15,15 @@ const { clamp, interpolateVec3 } = utilities; * Canvas referenced by the color bar where the colormap is rendered. It may * show the full image range or only the VOI range. */ -class ColorBarCanvas { +class ColorbarCanvas { private _canvas: HTMLCanvasElement; - private _imageRange: ColorBarImageRange; - private _voiRange: ColorBarVOIRange; + private _imageRange: ColorbarImageRange; + private _voiRange: ColorbarVOIRange; private _colormap: Types.ColormapRegistration; private _showFullImageRange: boolean; - constructor(props: ColorBarCanvasProps) { - ColorBarCanvas.validateProps(props); + constructor(props: ColorbarCanvasProps) { + ColorbarCanvas.validateProps(props); const { colormap, @@ -54,15 +54,15 @@ class ColorBarCanvas { this.render(); } - public get size(): ColorBarSize { + public get size(): ColorbarSize { const { width, height } = this._canvas; return { width, height }; } - public set size(size: ColorBarSize) { + public set size(size: ColorbarSize) { const { _canvas: canvas } = this; - if (!isColorBarSizeValid(size) || areColorBarSizesEqual(canvas, size)) { + if (!isColorbarSizeValid(size) || areColorbarSizesEqual(canvas, size)) { return; } @@ -70,14 +70,14 @@ class ColorBarCanvas { this.render(); } - public get imageRange(): ColorBarImageRange { + public get imageRange(): ColorbarImageRange { return { ...this._imageRange }; } - public set imageRange(imageRange: ColorBarImageRange) { + public set imageRange(imageRange: ColorbarImageRange) { if ( !isRangeValid(imageRange) || - areColorBarRangesEqual(imageRange, this._imageRange) + areColorbarRangesEqual(imageRange, this._imageRange) ) { return; } @@ -86,14 +86,14 @@ class ColorBarCanvas { this.render(); } - public get voiRange(): ColorBarVOIRange { + public get voiRange(): ColorbarVOIRange { return { ...this._voiRange }; } - public set voiRange(voiRange: ColorBarVOIRange) { + public set voiRange(voiRange: ColorbarVOIRange) { if ( !isRangeValid(voiRange) || - areColorBarRangesEqual(voiRange, this._voiRange) + areColorbarRangesEqual(voiRange, this._voiRange) ) { return; } @@ -127,10 +127,10 @@ class ColorBarCanvas { parentElement?.removeChild(canvas); } - private static validateProps(props: ColorBarCanvasProps) { + private static validateProps(props: ColorbarCanvasProps) { const { size, imageRange, voiRange } = props; - if (size && !isColorBarSizeValid(size)) { + if (size && !isColorbarSizeValid(size)) { throw new Error('Invalid "size"'); } @@ -143,7 +143,7 @@ class ColorBarCanvas { } } - private _setCanvasSize(canvas: HTMLCanvasElement, size: ColorBarSize) { + private _setCanvasSize(canvas: HTMLCanvasElement, size: ColorbarSize) { const { width, height } = size; canvas.width = width; @@ -155,7 +155,7 @@ class ColorBarCanvas { }); } - private _createRootElement(size: ColorBarSize) { + private _createRootElement(size: ColorbarSize) { const canvas = document.createElement('canvas'); Object.assign(canvas.style, { @@ -282,4 +282,4 @@ class ColorBarCanvas { } } -export { ColorBarCanvas as default, ColorBarCanvas }; +export { ColorbarCanvas as default, ColorbarCanvas }; diff --git a/packages/core/src/ui/widgets/colorBar/ColorBarTicks.ts b/packages/core/src/ui/widgets/colorbar/ColorbarTicks.ts similarity index 89% rename from packages/core/src/ui/widgets/colorBar/ColorBarTicks.ts rename to packages/core/src/ui/widgets/colorbar/ColorbarTicks.ts index 950ef6db85..dc0d4e6b57 100644 --- a/packages/core/src/ui/widgets/colorBar/ColorBarTicks.ts +++ b/packages/core/src/ui/widgets/colorbar/ColorbarTicks.ts @@ -1,16 +1,16 @@ import type { - ColorBarImageRange, - ColorBarVOIRange, - ColorBarSize, - ColorBarTicksProps, + ColorbarImageRange, + ColorbarVOIRange, + ColorbarSize, + ColorbarTicksProps, } from './types'; import { - isColorBarSizeValid, + isColorbarSizeValid, isRangeValid, - areColorBarRangesEqual, - areColorBarSizesEqual, + areColorbarRangesEqual, + areColorbarSizesEqual, } from './common'; -import { ColorBarRangeTextPosition } from './enums/ColorBarRangeTextPosition'; +import { ColorbarRangeTextPosition } from './enums/ColorbarRangeTextPosition'; const DEFAULTS = { FONT: '10px Arial', @@ -24,21 +24,21 @@ const DEFAULTS = { TICKS_STEPS: [1, 2.5, 5, 10], }; -class ColorBarTicks { +class ColorbarTicks { private _canvas: HTMLCanvasElement; - private _imageRange: ColorBarImageRange; - private _voiRange: ColorBarVOIRange; + private _imageRange: ColorbarImageRange; + private _voiRange: ColorbarVOIRange; private _color: string; private _tickSize: number; private _tickWidth: number; private _labelMargin: number; private _maxNumTicks: number; - private _rangeTextPosition: ColorBarRangeTextPosition; + private _rangeTextPosition: ColorbarRangeTextPosition; private _showFullPixelValueRange: boolean; private _font: string; - constructor(props: ColorBarTicksProps) { - ColorBarTicks.validateProps(props); + constructor(props: ColorbarTicksProps) { + ColorbarTicks.validateProps(props); const { top = 0, @@ -62,7 +62,7 @@ class ColorBarTicks { this._labelMargin = ticksStyle?.labelMargin ?? DEFAULTS.TICK_LABEL_MARGIN; this._maxNumTicks = ticksStyle?.maxNumTicks ?? DEFAULTS.MAX_NUM_TICKS; this._rangeTextPosition = - rangeTextPosition ?? ColorBarRangeTextPosition.Right; + rangeTextPosition ?? ColorbarRangeTextPosition.Right; this._showFullPixelValueRange = showFullPixelValueRange; this._canvas = this._createCanvasElement(size, top, left); @@ -71,15 +71,15 @@ class ColorBarTicks { } } - public get size(): ColorBarSize { + public get size(): ColorbarSize { const { width, height } = this._canvas; return { width, height }; } - public set size(size: ColorBarSize) { + public set size(size: ColorbarSize) { const { _canvas: canvas } = this; - if (!isColorBarSizeValid(size) || areColorBarSizesEqual(canvas, size)) { + if (!isColorbarSizeValid(size) || areColorbarSizesEqual(canvas, size)) { return; } @@ -141,10 +141,10 @@ class ColorBarTicks { /** * Set the image range that should goes from minPixelValue to maxPixelValue */ - public set imageRange(imageRange: ColorBarVOIRange) { + public set imageRange(imageRange: ColorbarVOIRange) { if ( !isRangeValid(imageRange) || - areColorBarRangesEqual(imageRange, this._imageRange) + areColorbarRangesEqual(imageRange, this._imageRange) ) { return; } @@ -165,10 +165,10 @@ class ColorBarTicks { * Set the VOI Range * (lower: wc - ww / 2, upper: wc + ww / 2) */ - public set voiRange(voiRange: ColorBarVOIRange) { + public set voiRange(voiRange: ColorbarVOIRange) { if ( !isRangeValid(voiRange) || - areColorBarRangesEqual(voiRange, this._voiRange) + areColorbarRangesEqual(voiRange, this._voiRange) ) { return; } @@ -294,10 +294,10 @@ class ColorBarTicks { this.render(); } - private static validateProps(props: ColorBarTicksProps) { + private static validateProps(props: ColorbarTicksProps) { const { size, imageRange, voiRange } = props; - if (size && !isColorBarSizeValid(size)) { + if (size && !isColorbarSizeValid(size)) { throw new Error('Invalid "size"'); } @@ -310,7 +310,7 @@ class ColorBarTicks { } } - private _setCanvasSize(canvas: HTMLCanvasElement, size: ColorBarSize) { + private _setCanvasSize(canvas: HTMLCanvasElement, size: ColorbarSize) { const { width, height } = size; canvas.width = width; @@ -323,7 +323,7 @@ class ColorBarTicks { } private _createCanvasElement( - size: ColorBarSize, + size: ColorbarSize, top: number, left: number ): HTMLCanvasElement { @@ -456,7 +456,7 @@ class ColorBarTicks { maxCanvasPixelValue * ((tick - range.lower) / rangeWidth) ); - // Zero at the bottom and max at the top of the colorBar on vertical colorBar + // Zero at the bottom and max at the top on vertical colorbars if (!isHorizontal) { position = height - position; } @@ -470,13 +470,13 @@ class ColorBarTicks { let tickInfo; if (isHorizontal) { - if (this._rangeTextPosition === ColorBarRangeTextPosition.Top) { + if (this._rangeTextPosition === ColorbarRangeTextPosition.Top) { tickInfo = this._getTopTickInfo({ position, labelMeasure }); } else { tickInfo = this._getBottomTickInfo({ position, labelMeasure }); } } else { - if (this._rangeTextPosition === ColorBarRangeTextPosition.Left) { + if (this._rangeTextPosition === ColorbarRangeTextPosition.Left) { tickInfo = this._getLeftTickInfo({ position, labelMeasure }); } else { tickInfo = this._getRightTickInfo({ position }); @@ -497,4 +497,4 @@ class ColorBarTicks { } } -export { ColorBarTicks as default, ColorBarTicks }; +export { ColorbarTicks as default, ColorbarTicks }; diff --git a/packages/core/src/ui/widgets/colorBar/ViewportColorBar.ts b/packages/core/src/ui/widgets/colorbar/ViewportColorbar.ts similarity index 88% rename from packages/core/src/ui/widgets/colorBar/ViewportColorBar.ts rename to packages/core/src/ui/widgets/colorbar/ViewportColorbar.ts index a5acd4d500..7c82804f81 100644 --- a/packages/core/src/ui/widgets/colorBar/ViewportColorBar.ts +++ b/packages/core/src/ui/widgets/colorbar/ViewportColorbar.ts @@ -7,23 +7,23 @@ import { utilities, getEnabledElement, } from '@cornerstonejs/core'; -import { ColorBar } from './ColorBar'; -import type { ViewportColorBarProps, ColorBarVOIRange } from './types'; +import { Colorbar } from './Colorbar'; +import type { ViewportColorbarProps, ColorbarVOIRange } from './types'; const { Events } = Enums; const defaultImageRange = { lower: -1000, upper: 1000 }; -class ViewportColorBar extends ColorBar { +class ViewportColorbar extends Colorbar { private _element: HTMLDivElement; private _volumeId: string; private _hideTicksTime: number; private _hideTicksTimeoutId: number; - constructor(props: ViewportColorBarProps) { + constructor(props: ViewportColorbarProps) { const { element, volumeId } = props; - const imageRange = ViewportColorBar._getImageRange(element, volumeId); - const voiRange = ViewportColorBar._getVOIRange(element, volumeId); + const imageRange = ViewportColorbar._getImageRange(element, volumeId); + const voiRange = ViewportColorbar._getVOIRange(element, volumeId); super({ ...props, imageRange, voiRange }); @@ -46,7 +46,7 @@ class ViewportColorBar extends ColorBar { return utilities.getVOIMultipliers(viewport, this._volumeId); } - protected onVoiChange(voiRange: ColorBarVOIRange) { + protected onVoiChange(voiRange: ColorbarVOIRange) { super.onVoiChange(voiRange); const { viewport } = this.enabledElement; @@ -137,7 +137,7 @@ class ViewportColorBar extends ColorBar { } private _stackNewImageCallback = () => { - this.imageRange = ViewportColorBar._getImageRange(this._element); + this.imageRange = ViewportColorbar._getImageRange(this._element); }; private _imageVolumeModifiedCallback = ( @@ -150,7 +150,7 @@ class ViewportColorBar extends ColorBar { } const { _element: element } = this; - this.imageRange = ViewportColorBar._getImageRange(element, volumeId); + this.imageRange = ViewportColorbar._getImageRange(element, volumeId); }; private _viewportVOIModifiedCallback = ( @@ -187,4 +187,4 @@ class ViewportColorBar extends ColorBar { } } -export { ViewportColorBar as default, ViewportColorBar as ViewportColorBar }; +export { ViewportColorbar as default, ViewportColorbar }; diff --git a/packages/core/src/ui/widgets/colorbar/common/areColorbarRangesEqual.ts b/packages/core/src/ui/widgets/colorbar/common/areColorbarRangesEqual.ts new file mode 100644 index 0000000000..aa7d508667 --- /dev/null +++ b/packages/core/src/ui/widgets/colorbar/common/areColorbarRangesEqual.ts @@ -0,0 +1,10 @@ +import type { ColorbarImageRange } from '../types/ColorbarImageRange'; + +const areColorbarRangesEqual = ( + a: ColorbarImageRange, + b: ColorbarImageRange +) => { + return !!a && !!b && a.lower === b.lower && a.upper === b.upper; +}; + +export { areColorbarRangesEqual as default, areColorbarRangesEqual }; diff --git a/packages/core/src/ui/widgets/colorbar/common/areColorbarSizesEqual.ts b/packages/core/src/ui/widgets/colorbar/common/areColorbarSizesEqual.ts new file mode 100644 index 0000000000..d06142bbbc --- /dev/null +++ b/packages/core/src/ui/widgets/colorbar/common/areColorbarSizesEqual.ts @@ -0,0 +1,7 @@ +import type { ColorbarSize } from '../types/ColorbarSize'; + +const areColorbarSizesEqual = (a: ColorbarSize, b: ColorbarSize) => { + return !!a && !!b && a.width === b.width && a.height === b.height; +}; + +export { areColorbarSizesEqual as default, areColorbarSizesEqual }; diff --git a/packages/core/src/ui/widgets/colorbar/common/index.ts b/packages/core/src/ui/widgets/colorbar/common/index.ts new file mode 100644 index 0000000000..3258d46462 --- /dev/null +++ b/packages/core/src/ui/widgets/colorbar/common/index.ts @@ -0,0 +1,4 @@ +export { isRangeValid } from './isRangeValid'; +export { isColorbarSizeValid } from './isColorbarSizeValid'; +export { areColorbarRangesEqual } from './areColorbarRangesEqual'; +export { areColorbarSizesEqual } from './areColorbarSizesEqual'; diff --git a/packages/core/src/ui/widgets/colorbar/common/isColorbarSizeValid.ts b/packages/core/src/ui/widgets/colorbar/common/isColorbarSizeValid.ts new file mode 100644 index 0000000000..96b268bf1f --- /dev/null +++ b/packages/core/src/ui/widgets/colorbar/common/isColorbarSizeValid.ts @@ -0,0 +1,7 @@ +import type { ColorbarSize } from '../types/ColorbarSize'; + +const isColorbarSizeValid = (size: ColorbarSize) => { + return !!size && size.width > 0 && size.height > 0; +}; + +export { isColorbarSizeValid as default, isColorbarSizeValid }; diff --git a/packages/core/src/ui/widgets/colorBar/common/isRangeTextPositionValid.ts b/packages/core/src/ui/widgets/colorbar/common/isRangeTextPositionValid.ts similarity index 60% rename from packages/core/src/ui/widgets/colorBar/common/isRangeTextPositionValid.ts rename to packages/core/src/ui/widgets/colorbar/common/isRangeTextPositionValid.ts index e36ff29709..d052806ebf 100644 --- a/packages/core/src/ui/widgets/colorBar/common/isRangeTextPositionValid.ts +++ b/packages/core/src/ui/widgets/colorbar/common/isRangeTextPositionValid.ts @@ -1,14 +1,14 @@ -import { ColorBarRangeTextPosition } from '../enums'; +import { ColorbarRangeTextPosition } from '../enums'; function isRangeTextPositionValid( colorbarWidth: number, colorbarHeight: number, - rangeTextPosition: ColorBarRangeTextPosition + rangeTextPosition: ColorbarRangeTextPosition ) { const isHorizontal = colorbarWidth >= colorbarHeight; const validRangeTextPositions = isHorizontal - ? [ColorBarRangeTextPosition.Top, ColorBarRangeTextPosition.Bottom] - : [ColorBarRangeTextPosition.Left, ColorBarRangeTextPosition.Right]; + ? [ColorbarRangeTextPosition.Top, ColorbarRangeTextPosition.Bottom] + : [ColorbarRangeTextPosition.Left, ColorbarRangeTextPosition.Right]; return validRangeTextPositions.includes(rangeTextPosition); } diff --git a/packages/core/src/ui/widgets/colorbar/common/isRangeValid.ts b/packages/core/src/ui/widgets/colorbar/common/isRangeValid.ts new file mode 100644 index 0000000000..9eb2c7812d --- /dev/null +++ b/packages/core/src/ui/widgets/colorbar/common/isRangeValid.ts @@ -0,0 +1,7 @@ +import type { ColorbarImageRange } from '../types/ColorbarImageRange'; + +const isRangeValid = (range: ColorbarImageRange) => { + return range && range.upper > range.lower; +}; + +export { isRangeValid as default, isRangeValid }; diff --git a/packages/core/src/ui/widgets/colorBar/enums/ColorBarRangeTextPosition.ts b/packages/core/src/ui/widgets/colorbar/enums/ColorbarRangeTextPosition.ts similarity index 85% rename from packages/core/src/ui/widgets/colorBar/enums/ColorBarRangeTextPosition.ts rename to packages/core/src/ui/widgets/colorbar/enums/ColorbarRangeTextPosition.ts index c4e72d2e7c..9cb6baea83 100644 --- a/packages/core/src/ui/widgets/colorBar/enums/ColorBarRangeTextPosition.ts +++ b/packages/core/src/ui/widgets/colorbar/enums/ColorbarRangeTextPosition.ts @@ -3,7 +3,7 @@ * Left/Right are the valid options for a vertical colorbars and Top/Bottom * for the horizontal ones. */ -export enum ColorBarRangeTextPosition { +export enum ColorbarRangeTextPosition { Top = 'top', Left = 'left', Bottom = 'bottom', diff --git a/packages/core/src/ui/widgets/colorbar/enums/index.ts b/packages/core/src/ui/widgets/colorbar/enums/index.ts new file mode 100644 index 0000000000..6e461586b4 --- /dev/null +++ b/packages/core/src/ui/widgets/colorbar/enums/index.ts @@ -0,0 +1 @@ +export { ColorbarRangeTextPosition } from './ColorbarRangeTextPosition'; diff --git a/packages/core/src/ui/widgets/colorbar/index.ts b/packages/core/src/ui/widgets/colorbar/index.ts new file mode 100644 index 0000000000..a724b78ddd --- /dev/null +++ b/packages/core/src/ui/widgets/colorbar/index.ts @@ -0,0 +1,5 @@ +export * as Enums from './enums'; +export type * as Types from './types'; + +export { Colorbar } from './Colorbar'; +export { ViewportColorbar } from './ViewportColorbar'; diff --git a/packages/core/src/ui/widgets/colorbar/types/ColorbarCanvasProps.ts b/packages/core/src/ui/widgets/colorbar/types/ColorbarCanvasProps.ts new file mode 100644 index 0000000000..dd606da410 --- /dev/null +++ b/packages/core/src/ui/widgets/colorbar/types/ColorbarCanvasProps.ts @@ -0,0 +1,14 @@ +import { Types } from '@cornerstonejs/core'; +import type { ColorbarImageRange } from './ColorbarImageRange'; +import type { ColorbarSize } from './ColorbarSize'; +import type { ColorbarVOIRange } from './ColorbarVOIRange'; + +export interface ColorbarCanvasProps { + colormap: Types.ColormapRegistration; + size?: ColorbarSize; + imageRange?: ColorbarImageRange; + voiRange?: ColorbarVOIRange; + + container?: HTMLElement; + showFullPixelValueRange?: boolean; +} diff --git a/packages/core/src/ui/widgets/colorBar/types/ColorBarCommonProps.ts b/packages/core/src/ui/widgets/colorbar/types/ColorbarCommonProps.ts similarity index 63% rename from packages/core/src/ui/widgets/colorBar/types/ColorBarCommonProps.ts rename to packages/core/src/ui/widgets/colorbar/types/ColorbarCommonProps.ts index bf51b65ea6..7a4e77c5e3 100644 --- a/packages/core/src/ui/widgets/colorBar/types/ColorBarCommonProps.ts +++ b/packages/core/src/ui/widgets/colorbar/types/ColorbarCommonProps.ts @@ -1,21 +1,21 @@ -import { ColorBarRangeTextPosition } from '../enums/ColorBarRangeTextPosition'; +import { ColorbarRangeTextPosition } from '../enums/ColorbarRangeTextPosition'; import type { - ColorBarImageRange, - ColorBarTicksStyle, - ColorBarVOIRange, + ColorbarImageRange, + ColorbarTicksStyle, + ColorbarVOIRange, } from '.'; -export type ColorBarCommonProps = { +export type ColorbarCommonProps = { // Image range from minPixelValue (lower) to maxPixelValue (upper) - imageRange?: ColorBarImageRange; + imageRange?: ColorbarImageRange; // VOI Range that is related to Window Width and Window Center - voiRange?: ColorBarVOIRange; + voiRange?: ColorbarVOIRange; // Ticks props ticks?: { // Position where the range text (tiks) should be displayed related to the ticks bar - position?: ColorBarRangeTextPosition; + position?: ColorbarRangeTextPosition; // Ticks style - style?: ColorBarTicksStyle; + style?: ColorbarTicksStyle; }; // The color bar shall show a range from `imageRange.lower` to `imageRange.upper` // when it is set to `true` or from `voiRange.lower` to `voiRange.upper` otherwise. diff --git a/packages/core/src/ui/widgets/colorBar/types/ColorBarImageRange.ts b/packages/core/src/ui/widgets/colorbar/types/ColorbarImageRange.ts similarity index 51% rename from packages/core/src/ui/widgets/colorBar/types/ColorBarImageRange.ts rename to packages/core/src/ui/widgets/colorbar/types/ColorbarImageRange.ts index 4561ec842d..eb26a58718 100644 --- a/packages/core/src/ui/widgets/colorBar/types/ColorBarImageRange.ts +++ b/packages/core/src/ui/widgets/colorbar/types/ColorbarImageRange.ts @@ -1,4 +1,4 @@ -export type ColorBarImageRange = { +export type ColorbarImageRange = { lower: number; upper: number; }; diff --git a/packages/core/src/ui/widgets/colorBar/types/ColorBarProps.ts b/packages/core/src/ui/widgets/colorbar/types/ColorbarProps.ts similarity index 62% rename from packages/core/src/ui/widgets/colorBar/types/ColorBarProps.ts rename to packages/core/src/ui/widgets/colorbar/types/ColorbarProps.ts index bb9c775d58..6324d381a4 100644 --- a/packages/core/src/ui/widgets/colorBar/types/ColorBarProps.ts +++ b/packages/core/src/ui/widgets/colorbar/types/ColorbarProps.ts @@ -1,8 +1,8 @@ import { Types } from '@cornerstonejs/core'; import type { WidgetProps } from '../../types'; -import { ColorBarCommonProps } from '.'; +import { ColorbarCommonProps } from '.'; -export type ColorBarProps = (WidgetProps & ColorBarCommonProps) & { +export type ColorbarProps = (WidgetProps & ColorbarCommonProps) & { colormaps: Types.ColormapRegistration[]; activeColormapName?: string; }; diff --git a/packages/core/src/ui/widgets/colorBar/types/ColorBarSize.ts b/packages/core/src/ui/widgets/colorbar/types/ColorbarSize.ts similarity index 56% rename from packages/core/src/ui/widgets/colorBar/types/ColorBarSize.ts rename to packages/core/src/ui/widgets/colorbar/types/ColorbarSize.ts index 95d6dc4e02..86538100d2 100644 --- a/packages/core/src/ui/widgets/colorBar/types/ColorBarSize.ts +++ b/packages/core/src/ui/widgets/colorbar/types/ColorbarSize.ts @@ -1,4 +1,4 @@ -export type ColorBarSize = { +export type ColorbarSize = { width: number; height: number; }; diff --git a/packages/core/src/ui/widgets/colorbar/types/ColorbarTicksProps.ts b/packages/core/src/ui/widgets/colorbar/types/ColorbarTicksProps.ts new file mode 100644 index 0000000000..298f1a9591 --- /dev/null +++ b/packages/core/src/ui/widgets/colorbar/types/ColorbarTicksProps.ts @@ -0,0 +1,8 @@ +import type { ColorbarCommonProps, ColorbarSize } from '.'; + +export type ColorbarTicksProps = ColorbarCommonProps & { + top?: number; + left?: number; + size?: ColorbarSize; + container?: HTMLElement; +}; diff --git a/packages/core/src/ui/widgets/colorBar/types/ColorBarTicksStyle.ts b/packages/core/src/ui/widgets/colorbar/types/ColorbarTicksStyle.ts similarity index 78% rename from packages/core/src/ui/widgets/colorBar/types/ColorBarTicksStyle.ts rename to packages/core/src/ui/widgets/colorbar/types/ColorbarTicksStyle.ts index 95bfca3503..cdcfaa48df 100644 --- a/packages/core/src/ui/widgets/colorBar/types/ColorBarTicksStyle.ts +++ b/packages/core/src/ui/widgets/colorbar/types/ColorbarTicksStyle.ts @@ -1,4 +1,4 @@ -export type ColorBarTicksStyle = { +export type ColorbarTicksStyle = { font?: string; color?: string; tickSize?: number; diff --git a/packages/core/src/ui/widgets/colorbar/types/ColorbarVOIRange.ts b/packages/core/src/ui/widgets/colorbar/types/ColorbarVOIRange.ts new file mode 100644 index 0000000000..8deaaab321 --- /dev/null +++ b/packages/core/src/ui/widgets/colorbar/types/ColorbarVOIRange.ts @@ -0,0 +1,3 @@ +import type { ColorbarImageRange } from './ColorbarImageRange'; + +export type ColorbarVOIRange = ColorbarImageRange; diff --git a/packages/core/src/ui/widgets/colorbar/types/ViewportColorbarProps.ts b/packages/core/src/ui/widgets/colorbar/types/ViewportColorbarProps.ts new file mode 100644 index 0000000000..d1a71b07fa --- /dev/null +++ b/packages/core/src/ui/widgets/colorbar/types/ViewportColorbarProps.ts @@ -0,0 +1,6 @@ +import type { ColorbarProps } from './ColorbarProps'; + +export type ViewportColorbarProps = ColorbarProps & { + element: HTMLDivElement; + volumeId?: string; +}; diff --git a/packages/core/src/ui/widgets/colorbar/types/index.ts b/packages/core/src/ui/widgets/colorbar/types/index.ts new file mode 100644 index 0000000000..c6946985fe --- /dev/null +++ b/packages/core/src/ui/widgets/colorbar/types/index.ts @@ -0,0 +1,8 @@ +export type { ColorbarCommonProps } from './ColorbarCommonProps'; +export type { ColorbarProps } from './ColorbarProps'; +export type { ColorbarImageRange } from './ColorbarImageRange'; +export type { ColorbarVOIRange } from './ColorbarVOIRange'; +export type { ColorbarSize } from './ColorbarSize'; +export type { ColorbarTicksProps } from './ColorbarTicksProps'; +export type { ColorbarTicksStyle } from './ColorbarTicksStyle'; +export type { ViewportColorbarProps } from './ViewportColorbarProps'; diff --git a/packages/core/src/ui/widgets/index.ts b/packages/core/src/ui/widgets/index.ts index 9ab9b6c816..381f2c4f09 100644 --- a/packages/core/src/ui/widgets/index.ts +++ b/packages/core/src/ui/widgets/index.ts @@ -1 +1 @@ -export * as colorbar from './colorBar'; +export * as colorbar from './colorbar'; From 847e38005e50c3a58e5fe35ad939e29b0a3055cc Mon Sep 17 00:00:00 2001 From: Leonardo Campos Date: Wed, 25 Oct 2023 00:02:23 -0300 Subject: [PATCH 08/14] added some comments --- packages/core/src/ui/widgets/colorbar/Colorbar.ts | 5 +++++ packages/core/src/ui/widgets/colorbar/ViewportColorbar.ts | 5 ++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/packages/core/src/ui/widgets/colorbar/Colorbar.ts b/packages/core/src/ui/widgets/colorbar/Colorbar.ts index 1edd13c268..ad0373d977 100644 --- a/packages/core/src/ui/widgets/colorbar/Colorbar.ts +++ b/packages/core/src/ui/widgets/colorbar/Colorbar.ts @@ -22,6 +22,11 @@ type ColorbarPoints = { local: Types.Point2; }; +/** + * A base colorbar class that is not associated with any viewport. It is + * possible to click and drag to change the VOI range, shows the ticks during + * interaction and it can show full image range or VOI range. + */ class Colorbar extends Widget { private _colormaps: Map; private _activeColormapName: string; diff --git a/packages/core/src/ui/widgets/colorbar/ViewportColorbar.ts b/packages/core/src/ui/widgets/colorbar/ViewportColorbar.ts index 7c82804f81..61bda7e066 100644 --- a/packages/core/src/ui/widgets/colorbar/ViewportColorbar.ts +++ b/packages/core/src/ui/widgets/colorbar/ViewportColorbar.ts @@ -12,7 +12,10 @@ import type { ViewportColorbarProps, ColorbarVOIRange } from './types'; const { Events } = Enums; const defaultImageRange = { lower: -1000, upper: 1000 }; - +/** + * A colorbar associated with a viewport that updates automatically when the + * viewport VOI changes or when the stack/volume are updated.. + */ class ViewportColorbar extends Colorbar { private _element: HTMLDivElement; private _volumeId: string; From 78e81d4e1225abf4f71643bc487ab123d4879e69 Mon Sep 17 00:00:00 2001 From: Leonardo Campos Date: Wed, 25 Oct 2023 11:55:53 -0300 Subject: [PATCH 09/14] code review --- packages/core/src/index.ts | 3 --- packages/core/src/ui/index.ts | 1 - packages/core/src/utilities/index.ts | 6 ----- .../core/src/utilities/isViewportPreScaled.ts | 19 --------------- .../examples/colorbar/index.ts | 13 ++++------ .../src/utilities/getVOIMultipliers.ts | 12 +++++----- packages/tools/src/utilities/index.ts | 2 ++ .../utilities/math/vec3}/interpolateVec3.ts | 6 ++--- .../utilities/viewport/isViewportPreScaled.ts | 4 +++- packages/tools/src/utilities/voi/index.ts | 1 + .../src/utilities/voi}/widgets/Widget.ts | 0 .../voi}/widgets/colorbar/Colorbar.ts | 24 +++++++++++-------- .../voi}/widgets/colorbar/ColorbarCanvas.ts | 12 ++++++---- .../voi}/widgets/colorbar/ColorbarTicks.ts | 0 .../voi}/widgets/colorbar/ViewportColorbar.ts | 3 ++- .../colorbar/common/areColorbarRangesEqual.ts | 0 .../colorbar/common/areColorbarSizesEqual.ts | 0 .../voi}/widgets/colorbar/common/index.ts | 0 .../colorbar/common/isColorbarSizeValid.ts | 0 .../common/isRangeTextPositionValid.ts | 0 .../widgets/colorbar/common/isRangeValid.ts | 0 .../enums/ColorbarRangeTextPosition.ts | 0 .../voi}/widgets/colorbar/enums/index.ts | 0 .../utilities/voi}/widgets/colorbar/index.ts | 0 .../colorbar/types/ColorbarCanvasProps.ts | 4 ++-- .../colorbar/types/ColorbarCommonProps.ts | 0 .../colorbar/types/ColorbarImageRange.ts | 0 .../widgets/colorbar/types/ColorbarProps.ts | 4 ++-- .../widgets/colorbar/types/ColorbarSize.ts | 0 .../colorbar/types/ColorbarTicksProps.ts | 0 .../colorbar/types/ColorbarTicksStyle.ts | 0 .../colorbar/types/ColorbarVOIRange.ts | 0 .../colorbar/types/ViewportColorbarProps.ts | 0 .../voi}/widgets/colorbar/types/index.ts | 0 .../src/utilities/voi}/widgets/index.ts | 0 .../voi}/widgets/types/WidgetProps.ts | 0 .../voi}/widgets/types/WidgetSize.ts | 0 .../src/utilities/voi}/widgets/types/index.ts | 0 38 files changed, 47 insertions(+), 67 deletions(-) delete mode 100644 packages/core/src/ui/index.ts delete mode 100644 packages/core/src/utilities/isViewportPreScaled.ts rename packages/{core => tools}/examples/colorbar/index.ts (98%) rename packages/{core => tools}/src/utilities/getVOIMultipliers.ts (66%) rename packages/{core/src/utilities => tools/src/utilities/math/vec3}/interpolateVec3.ts (66%) create mode 100644 packages/tools/src/utilities/voi/index.ts rename packages/{core/src/ui => tools/src/utilities/voi}/widgets/Widget.ts (100%) rename packages/{core/src/ui => tools/src/utilities/voi}/widgets/colorbar/Colorbar.ts (93%) rename packages/{core/src/ui => tools/src/utilities/voi}/widgets/colorbar/ColorbarCanvas.ts (95%) rename packages/{core/src/ui => tools/src/utilities/voi}/widgets/colorbar/ColorbarTicks.ts (100%) rename packages/{core/src/ui => tools/src/utilities/voi}/widgets/colorbar/ViewportColorbar.ts (97%) rename packages/{core/src/ui => tools/src/utilities/voi}/widgets/colorbar/common/areColorbarRangesEqual.ts (100%) rename packages/{core/src/ui => tools/src/utilities/voi}/widgets/colorbar/common/areColorbarSizesEqual.ts (100%) rename packages/{core/src/ui => tools/src/utilities/voi}/widgets/colorbar/common/index.ts (100%) rename packages/{core/src/ui => tools/src/utilities/voi}/widgets/colorbar/common/isColorbarSizeValid.ts (100%) rename packages/{core/src/ui => tools/src/utilities/voi}/widgets/colorbar/common/isRangeTextPositionValid.ts (100%) rename packages/{core/src/ui => tools/src/utilities/voi}/widgets/colorbar/common/isRangeValid.ts (100%) rename packages/{core/src/ui => tools/src/utilities/voi}/widgets/colorbar/enums/ColorbarRangeTextPosition.ts (100%) rename packages/{core/src/ui => tools/src/utilities/voi}/widgets/colorbar/enums/index.ts (100%) rename packages/{core/src/ui => tools/src/utilities/voi}/widgets/colorbar/index.ts (100%) rename packages/{core/src/ui => tools/src/utilities/voi}/widgets/colorbar/types/ColorbarCanvasProps.ts (74%) rename packages/{core/src/ui => tools/src/utilities/voi}/widgets/colorbar/types/ColorbarCommonProps.ts (100%) rename packages/{core/src/ui => tools/src/utilities/voi}/widgets/colorbar/types/ColorbarImageRange.ts (100%) rename packages/{core/src/ui => tools/src/utilities/voi}/widgets/colorbar/types/ColorbarProps.ts (59%) rename packages/{core/src/ui => tools/src/utilities/voi}/widgets/colorbar/types/ColorbarSize.ts (100%) rename packages/{core/src/ui => tools/src/utilities/voi}/widgets/colorbar/types/ColorbarTicksProps.ts (100%) rename packages/{core/src/ui => tools/src/utilities/voi}/widgets/colorbar/types/ColorbarTicksStyle.ts (100%) rename packages/{core/src/ui => tools/src/utilities/voi}/widgets/colorbar/types/ColorbarVOIRange.ts (100%) rename packages/{core/src/ui => tools/src/utilities/voi}/widgets/colorbar/types/ViewportColorbarProps.ts (100%) rename packages/{core/src/ui => tools/src/utilities/voi}/widgets/colorbar/types/index.ts (100%) rename packages/{core/src/ui => tools/src/utilities/voi}/widgets/index.ts (100%) rename packages/{core/src/ui => tools/src/utilities/voi}/widgets/types/WidgetProps.ts (100%) rename packages/{core/src/ui => tools/src/utilities/voi}/widgets/types/WidgetSize.ts (100%) rename packages/{core/src/ui => tools/src/utilities/voi}/widgets/types/index.ts (100%) diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index be46110de6..fb9614ec42 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -50,7 +50,6 @@ import * as imageLoader from './loaders/imageLoader'; import * as geometryLoader from './loaders/geometryLoader'; import * as Types from './types'; import * as utilities from './utilities'; -import * as ui from './ui'; import { registerImageLoader } from './loaders/imageLoader'; // since it is used by CSWIL right now import triggerEvent from './utilities/triggerEvent'; @@ -123,6 +122,4 @@ export { resetUseSharedArrayBuffer, // Geometry Loader geometryLoader, - // User Interface - ui, }; diff --git a/packages/core/src/ui/index.ts b/packages/core/src/ui/index.ts deleted file mode 100644 index 0a512415b7..0000000000 --- a/packages/core/src/ui/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * as widgets from './widgets'; diff --git a/packages/core/src/utilities/index.ts b/packages/core/src/utilities/index.ts index df97505e6f..579f82ee6e 100644 --- a/packages/core/src/utilities/index.ts +++ b/packages/core/src/utilities/index.ts @@ -12,20 +12,17 @@ import imageIdToURI from './imageIdToURI'; import calibratedPixelSpacingMetadataProvider from './calibratedPixelSpacingMetadataProvider'; import clamp from './clamp'; import isEqual from './isEqual'; -import isViewportPreScaled from './isViewportPreScaled'; import isOpposite from './isOpposite'; import createUint8SharedArray from './createUint8SharedArray'; import createFloat32SharedArray from './createFloat32SharedArray'; import createUint16SharedArray from './createUInt16SharedArray'; import createInt16SharedArray from './createInt16SharedArray'; import getViewportModality from './getViewportModality'; -import getVOIMultipliers from './getVOIMultipliers'; import getClosestImageId from './getClosestImageId'; import getSpacingInNormalDirection from './getSpacingInNormalDirection'; import getTargetVolumeAndSpacingInNormalDir from './getTargetVolumeAndSpacingInNormalDir'; import getVolumeActorCorners from './getVolumeActorCorners'; import indexWithinDimensions from './indexWithinDimensions'; -import interpolateVec3 from './interpolateVec3'; import getVolumeViewportsContainingSameVolumes from './getVolumeViewportsContainingSameVolumes'; import getViewportsWithVolumeId from './getViewportsWithVolumeId'; import transformWorldToIndex from './transformWorldToIndex'; @@ -75,21 +72,18 @@ export { getMinMax, getRuntimeId, isEqual, - isViewportPreScaled, isOpposite, createFloat32SharedArray, createUint8SharedArray, createUint16SharedArray, createInt16SharedArray, getViewportModality, - getVOIMultipliers, windowLevel, getClosestImageId, getSpacingInNormalDirection, getTargetVolumeAndSpacingInNormalDir, getVolumeActorCorners, indexWithinDimensions, - interpolateVec3, getVolumeViewportsContainingSameVolumes, getViewportsWithVolumeId, transformWorldToIndex, diff --git a/packages/core/src/utilities/isViewportPreScaled.ts b/packages/core/src/utilities/isViewportPreScaled.ts deleted file mode 100644 index e3547177ea..0000000000 --- a/packages/core/src/utilities/isViewportPreScaled.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { IViewport } from '../types'; -import { StackViewport, VolumeViewport } from '../RenderingEngine'; -import cache from '../cache'; - -function isViewportPreScaled(viewport: IViewport, volumeId?: string) { - if (viewport instanceof VolumeViewport) { - const volume = cache.getVolume(volumeId); - const { scaling } = volume; - - return !!scaling && Object.keys(scaling).length > 0; - } - - if (viewport instanceof StackViewport) { - const { preScale } = viewport.getImageData(); - return preScale.scaled && preScale.scalingParameters?.suvbw !== undefined; - } -} - -export { isViewportPreScaled as default, isViewportPreScaled }; diff --git a/packages/core/examples/colorbar/index.ts b/packages/tools/examples/colorbar/index.ts similarity index 98% rename from packages/core/examples/colorbar/index.ts rename to packages/tools/examples/colorbar/index.ts index 18c2f09d27..7459932926 100644 --- a/packages/core/examples/colorbar/index.ts +++ b/packages/tools/examples/colorbar/index.ts @@ -6,8 +6,8 @@ import { cache, volumeLoader, getRenderingEngine, - ui, } from '@cornerstonejs/core'; +import { utilities as cstUtils } from '@cornerstonejs/tools'; import { initDemo, createImageIdsAndCacheMetaData, @@ -18,8 +18,8 @@ import { } from '../../../../utils/demo/helpers'; import * as cornerstoneTools from '@cornerstonejs/tools'; -const { ViewportColorbar } = ui.widgets.colorbar; -const { ColorbarRangeTextPosition } = ui.widgets.colorbar.Enums; +const { ViewportColorbar } = cstUtils.voi.colorbar; +const { ColorbarRangeTextPosition } = cstUtils.voi.colorbar.Enums; // This is for debugging purposes console.warn( @@ -56,11 +56,8 @@ const ptVolumeId = `${volumeLoaderScheme}:${ptVolumeName}`; // Convert all VTK colormaps to the one supported by the colorbar which actualy // have almost the same properties. -const colormaps = vtkColormaps.rgbPresetNames.map( - (presetName) => - vtkColormaps.getPresetByName( - presetName - ) as unknown as Types.ColormapRegistration +const colormaps = vtkColormaps.rgbPresetNames.map((presetName) => + vtkColormaps.getPresetByName(presetName) ); // Colormap to load right after loading the example page but it can be changed diff --git a/packages/core/src/utilities/getVOIMultipliers.ts b/packages/tools/src/utilities/getVOIMultipliers.ts similarity index 66% rename from packages/core/src/utilities/getVOIMultipliers.ts rename to packages/tools/src/utilities/getVOIMultipliers.ts index 1d79d22bfb..3f3f008f2a 100644 --- a/packages/core/src/utilities/getVOIMultipliers.ts +++ b/packages/tools/src/utilities/getVOIMultipliers.ts @@ -1,22 +1,22 @@ -import * as utils from '.'; -import { IViewport } from '../types'; +import { Types, utilities as csUtils } from '@cornerstonejs/core'; +import { isViewportPreScaled } from './viewport'; const DEFAULT_MULTIPLIER = 4; function getVOIMultipliers( - viewport: IViewport, + viewport: Types.IStackViewport | Types.IVolumeViewport, volumeId?: string, options?: { fixedPTWindowWidth?: boolean; } ): [number, number] { - const modality = utils.getViewportModality(viewport, volumeId); - const isPreScaled = utils.isViewportPreScaled(viewport, volumeId); - const { fixedPTWindowWidth = true } = options ?? {}; + const modality = csUtils.getViewportModality(viewport, volumeId); if (modality === 'PT') { const { clientWidth, clientHeight } = viewport.element; const ptMultiplier = 5 / Math.max(clientWidth, clientHeight); + const isPreScaled = isViewportPreScaled(viewport, volumeId); + const { fixedPTWindowWidth = true } = options ?? {}; // Set the "X" multiplier equal to zero in order to do not allow // any change to the window width (0 * cursorDeltaX = 0) diff --git a/packages/tools/src/utilities/index.ts b/packages/tools/src/utilities/index.ts index 2fea467c05..ddcec30d99 100644 --- a/packages/tools/src/utilities/index.ts +++ b/packages/tools/src/utilities/index.ts @@ -35,6 +35,7 @@ import * as viewport from './viewport'; import * as touch from './touch'; import * as dynamicVolume from './dynamicVolume'; import * as polyDataUtils from './polyData/utils'; +import * as voi from './voi'; // Events import { triggerEvent } from '@cornerstonejs/core'; @@ -72,4 +73,5 @@ export { roundNumber, pointToString, polyDataUtils, + voi, }; diff --git a/packages/core/src/utilities/interpolateVec3.ts b/packages/tools/src/utilities/math/vec3/interpolateVec3.ts similarity index 66% rename from packages/core/src/utilities/interpolateVec3.ts rename to packages/tools/src/utilities/math/vec3/interpolateVec3.ts index a8443eee1e..dd58102516 100644 --- a/packages/core/src/utilities/interpolateVec3.ts +++ b/packages/tools/src/utilities/math/vec3/interpolateVec3.ts @@ -4,9 +4,9 @@ * @param a - First vec3 * @param b - Second vec3 * @param t - Time "t". - * Vector A is returned for values smaller than or equel to 0. - * Vector B is returned for values greater than or equal to 1. - * An interpolation between vector A and B is returned otherwise. + * - Vector A is returned for values smaller than or equel to 0. + * - Vector B is returned for values greater than or equal to 1. + * - An interpolation between vectors A and B is returned otherwise. * @returns */ const interpolateVec3 = (a, b, t) => { diff --git a/packages/tools/src/utilities/viewport/isViewportPreScaled.ts b/packages/tools/src/utilities/viewport/isViewportPreScaled.ts index d0c8c1c070..35ed0a651e 100644 --- a/packages/tools/src/utilities/viewport/isViewportPreScaled.ts +++ b/packages/tools/src/utilities/viewport/isViewportPreScaled.ts @@ -10,7 +10,9 @@ function isViewportPreScaled( targetId: string ): boolean { if (viewport instanceof BaseVolumeViewport) { - const volumeId = targetId.split('volumeId:')[1]; + const targetIdTokens = targetId.split('volumeId:'); + const volumeId = + targetIdTokens.length > 1 ? targetIdTokens[1] : targetIdTokens[0]; const volume = cache.getVolume(volumeId); return !!volume?.scaling && Object.keys(volume.scaling).length > 0; } else if (viewport instanceof StackViewport) { diff --git a/packages/tools/src/utilities/voi/index.ts b/packages/tools/src/utilities/voi/index.ts new file mode 100644 index 0000000000..31db82c42d --- /dev/null +++ b/packages/tools/src/utilities/voi/index.ts @@ -0,0 +1 @@ +export { colorbar } from './widgets'; diff --git a/packages/core/src/ui/widgets/Widget.ts b/packages/tools/src/utilities/voi/widgets/Widget.ts similarity index 100% rename from packages/core/src/ui/widgets/Widget.ts rename to packages/tools/src/utilities/voi/widgets/Widget.ts diff --git a/packages/core/src/ui/widgets/colorbar/Colorbar.ts b/packages/tools/src/utilities/voi/widgets/colorbar/Colorbar.ts similarity index 93% rename from packages/core/src/ui/widgets/colorbar/Colorbar.ts rename to packages/tools/src/utilities/voi/widgets/colorbar/Colorbar.ts index ad0373d977..92980a71c8 100644 --- a/packages/core/src/ui/widgets/colorbar/Colorbar.ts +++ b/packages/tools/src/utilities/voi/widgets/colorbar/Colorbar.ts @@ -1,5 +1,6 @@ +import { IColorMapPreset } from '@kitware/vtk.js/Rendering/Core/ColorTransferFunction/ColorMaps'; import { vec2 } from 'gl-matrix'; -import { utilities, Types } from '@cornerstonejs/core'; +import { utilities as csUtils, Types } from '@cornerstonejs/core'; import { Widget } from '../Widget'; import type { ColorbarProps, ColorbarVOIRange } from './types'; import { isRangeValid, areColorbarRangesEqual } from './common'; @@ -8,8 +9,6 @@ import { ColorbarCanvas } from './ColorbarCanvas'; import { ColorbarTicks } from './ColorbarTicks'; import isRangeTextPositionValid from './common/isRangeTextPositionValid'; -const { MultiTargetEventListenerManager } = utilities.eventListener; - const DEFAULTS = { MULTIPLIER: 1, RANGE_TEXT_POSITION: ColorbarRangeTextPosition.Right, @@ -28,9 +27,9 @@ type ColorbarPoints = { * interaction and it can show full image range or VOI range. */ class Colorbar extends Widget { - private _colormaps: Map; + private _colormaps: Map; private _activeColormapName: string; - private _eventListenersManager: MultiTargetEventListenerManager; + private _eventListenersManager: csUtils.eventListener.MultiTargetEventListenerManager; private _canvas: ColorbarCanvas; private _ticksBar: ColorbarTicks; private _rangeTextPosition: ColorbarRangeTextPosition; @@ -41,7 +40,8 @@ class Colorbar extends Widget { constructor(props: ColorbarProps) { super(props); - this._eventListenersManager = new MultiTargetEventListenerManager(); + this._eventListenersManager = + new csUtils.eventListener.MultiTargetEventListenerManager(); this._colormaps = Colorbar.getColormapsMap(props); this._activeColormapName = Colorbar.getInitialColormapName(props); this._canvas = this._createCanvas(props); @@ -165,7 +165,7 @@ class Colorbar extends Widget { return colormaps.reduce( (items, item) => items.set(item.Name, item), - new Map() + new Map() ); } @@ -304,7 +304,7 @@ class Colorbar extends Widget { } const { lower: voiLower, upper: voiUpper } = startVOIRange; - let { windowWidth, windowCenter } = utilities.windowLevel.toWindowLevel( + let { windowWidth, windowCenter } = csUtils.windowLevel.toWindowLevel( voiLower, voiUpper ); @@ -312,7 +312,7 @@ class Colorbar extends Widget { windowWidth = Math.max(windowWidth + wwDelta, 1); windowCenter += wcDelta; - const newVoiRange = utilities.windowLevel.toLowHighRange( + const newVoiRange = csUtils.windowLevel.toLowHighRange( windowWidth, windowCenter ); @@ -335,7 +335,11 @@ class Colorbar extends Widget { manager.addEventListener(element, 'mouseover', this._mouseOverCallback); manager.addEventListener(element, 'mouseout', this._mouseOutCallback); - manager.addEventListener(element, 'mousedown', this._mouseDownCallback); + manager.addEventListener( + element, + 'mousedown', + this._mouseDownCallback as EventListener + ); } private _addVOIEventListeners(evt: MouseEvent) { diff --git a/packages/core/src/ui/widgets/colorbar/ColorbarCanvas.ts b/packages/tools/src/utilities/voi/widgets/colorbar/ColorbarCanvas.ts similarity index 95% rename from packages/core/src/ui/widgets/colorbar/ColorbarCanvas.ts rename to packages/tools/src/utilities/voi/widgets/colorbar/ColorbarCanvas.ts index 9239fefd7d..9628acd4f5 100644 --- a/packages/core/src/ui/widgets/colorbar/ColorbarCanvas.ts +++ b/packages/tools/src/utilities/voi/widgets/colorbar/ColorbarCanvas.ts @@ -1,4 +1,6 @@ -import { Types, utilities } from '@cornerstonejs/core'; +import { IColorMapPreset } from '@kitware/vtk.js/Rendering/Core/ColorTransferFunction/ColorMaps'; +import { utilities } from '@cornerstonejs/core'; +import interpolateVec3 from '../../../math/vec3/interpolateVec3'; import { ColorbarCanvasProps } from './types/ColorbarCanvasProps'; import type { ColorbarImageRange, ColorbarVOIRange } from './types'; import type { ColorbarSize } from './types/ColorbarSize'; @@ -9,7 +11,7 @@ import { areColorbarSizesEqual, } from './common'; -const { clamp, interpolateVec3 } = utilities; +const { clamp } = utilities; /** * Canvas referenced by the color bar where the colormap is rendered. It may @@ -19,7 +21,7 @@ class ColorbarCanvas { private _canvas: HTMLCanvasElement; private _imageRange: ColorbarImageRange; private _voiRange: ColorbarVOIRange; - private _colormap: Types.ColormapRegistration; + private _colormap: IColorMapPreset; private _showFullImageRange: boolean; constructor(props: ColorbarCanvasProps) { @@ -45,11 +47,11 @@ class ColorbarCanvas { } } - public get colormap(): Types.ColormapRegistration { + public get colormap(): IColorMapPreset { return this._colormap; } - public set colormap(colormap: Types.ColormapRegistration) { + public set colormap(colormap: IColorMapPreset) { this._colormap = colormap; this.render(); } diff --git a/packages/core/src/ui/widgets/colorbar/ColorbarTicks.ts b/packages/tools/src/utilities/voi/widgets/colorbar/ColorbarTicks.ts similarity index 100% rename from packages/core/src/ui/widgets/colorbar/ColorbarTicks.ts rename to packages/tools/src/utilities/voi/widgets/colorbar/ColorbarTicks.ts diff --git a/packages/core/src/ui/widgets/colorbar/ViewportColorbar.ts b/packages/tools/src/utilities/voi/widgets/colorbar/ViewportColorbar.ts similarity index 97% rename from packages/core/src/ui/widgets/colorbar/ViewportColorbar.ts rename to packages/tools/src/utilities/voi/widgets/colorbar/ViewportColorbar.ts index 61bda7e066..c570a20c03 100644 --- a/packages/core/src/ui/widgets/colorbar/ViewportColorbar.ts +++ b/packages/tools/src/utilities/voi/widgets/colorbar/ViewportColorbar.ts @@ -9,6 +9,7 @@ import { } from '@cornerstonejs/core'; import { Colorbar } from './Colorbar'; import type { ViewportColorbarProps, ColorbarVOIRange } from './types'; +import { getVOIMultipliers } from '../../../getVOIMultipliers'; const { Events } = Enums; const defaultImageRange = { lower: -1000, upper: 1000 }; @@ -46,7 +47,7 @@ class ViewportColorbar extends Colorbar { protected getVOIMultipliers(): [number, number] { const { viewport } = this.enabledElement; - return utilities.getVOIMultipliers(viewport, this._volumeId); + return getVOIMultipliers(viewport, this._volumeId); } protected onVoiChange(voiRange: ColorbarVOIRange) { diff --git a/packages/core/src/ui/widgets/colorbar/common/areColorbarRangesEqual.ts b/packages/tools/src/utilities/voi/widgets/colorbar/common/areColorbarRangesEqual.ts similarity index 100% rename from packages/core/src/ui/widgets/colorbar/common/areColorbarRangesEqual.ts rename to packages/tools/src/utilities/voi/widgets/colorbar/common/areColorbarRangesEqual.ts diff --git a/packages/core/src/ui/widgets/colorbar/common/areColorbarSizesEqual.ts b/packages/tools/src/utilities/voi/widgets/colorbar/common/areColorbarSizesEqual.ts similarity index 100% rename from packages/core/src/ui/widgets/colorbar/common/areColorbarSizesEqual.ts rename to packages/tools/src/utilities/voi/widgets/colorbar/common/areColorbarSizesEqual.ts diff --git a/packages/core/src/ui/widgets/colorbar/common/index.ts b/packages/tools/src/utilities/voi/widgets/colorbar/common/index.ts similarity index 100% rename from packages/core/src/ui/widgets/colorbar/common/index.ts rename to packages/tools/src/utilities/voi/widgets/colorbar/common/index.ts diff --git a/packages/core/src/ui/widgets/colorbar/common/isColorbarSizeValid.ts b/packages/tools/src/utilities/voi/widgets/colorbar/common/isColorbarSizeValid.ts similarity index 100% rename from packages/core/src/ui/widgets/colorbar/common/isColorbarSizeValid.ts rename to packages/tools/src/utilities/voi/widgets/colorbar/common/isColorbarSizeValid.ts diff --git a/packages/core/src/ui/widgets/colorbar/common/isRangeTextPositionValid.ts b/packages/tools/src/utilities/voi/widgets/colorbar/common/isRangeTextPositionValid.ts similarity index 100% rename from packages/core/src/ui/widgets/colorbar/common/isRangeTextPositionValid.ts rename to packages/tools/src/utilities/voi/widgets/colorbar/common/isRangeTextPositionValid.ts diff --git a/packages/core/src/ui/widgets/colorbar/common/isRangeValid.ts b/packages/tools/src/utilities/voi/widgets/colorbar/common/isRangeValid.ts similarity index 100% rename from packages/core/src/ui/widgets/colorbar/common/isRangeValid.ts rename to packages/tools/src/utilities/voi/widgets/colorbar/common/isRangeValid.ts diff --git a/packages/core/src/ui/widgets/colorbar/enums/ColorbarRangeTextPosition.ts b/packages/tools/src/utilities/voi/widgets/colorbar/enums/ColorbarRangeTextPosition.ts similarity index 100% rename from packages/core/src/ui/widgets/colorbar/enums/ColorbarRangeTextPosition.ts rename to packages/tools/src/utilities/voi/widgets/colorbar/enums/ColorbarRangeTextPosition.ts diff --git a/packages/core/src/ui/widgets/colorbar/enums/index.ts b/packages/tools/src/utilities/voi/widgets/colorbar/enums/index.ts similarity index 100% rename from packages/core/src/ui/widgets/colorbar/enums/index.ts rename to packages/tools/src/utilities/voi/widgets/colorbar/enums/index.ts diff --git a/packages/core/src/ui/widgets/colorbar/index.ts b/packages/tools/src/utilities/voi/widgets/colorbar/index.ts similarity index 100% rename from packages/core/src/ui/widgets/colorbar/index.ts rename to packages/tools/src/utilities/voi/widgets/colorbar/index.ts diff --git a/packages/core/src/ui/widgets/colorbar/types/ColorbarCanvasProps.ts b/packages/tools/src/utilities/voi/widgets/colorbar/types/ColorbarCanvasProps.ts similarity index 74% rename from packages/core/src/ui/widgets/colorbar/types/ColorbarCanvasProps.ts rename to packages/tools/src/utilities/voi/widgets/colorbar/types/ColorbarCanvasProps.ts index dd606da410..f336a54267 100644 --- a/packages/core/src/ui/widgets/colorbar/types/ColorbarCanvasProps.ts +++ b/packages/tools/src/utilities/voi/widgets/colorbar/types/ColorbarCanvasProps.ts @@ -1,10 +1,10 @@ -import { Types } from '@cornerstonejs/core'; +import { IColorMapPreset } from '@kitware/vtk.js/Rendering/Core/ColorTransferFunction/ColorMaps'; import type { ColorbarImageRange } from './ColorbarImageRange'; import type { ColorbarSize } from './ColorbarSize'; import type { ColorbarVOIRange } from './ColorbarVOIRange'; export interface ColorbarCanvasProps { - colormap: Types.ColormapRegistration; + colormap: IColorMapPreset; size?: ColorbarSize; imageRange?: ColorbarImageRange; voiRange?: ColorbarVOIRange; diff --git a/packages/core/src/ui/widgets/colorbar/types/ColorbarCommonProps.ts b/packages/tools/src/utilities/voi/widgets/colorbar/types/ColorbarCommonProps.ts similarity index 100% rename from packages/core/src/ui/widgets/colorbar/types/ColorbarCommonProps.ts rename to packages/tools/src/utilities/voi/widgets/colorbar/types/ColorbarCommonProps.ts diff --git a/packages/core/src/ui/widgets/colorbar/types/ColorbarImageRange.ts b/packages/tools/src/utilities/voi/widgets/colorbar/types/ColorbarImageRange.ts similarity index 100% rename from packages/core/src/ui/widgets/colorbar/types/ColorbarImageRange.ts rename to packages/tools/src/utilities/voi/widgets/colorbar/types/ColorbarImageRange.ts diff --git a/packages/core/src/ui/widgets/colorbar/types/ColorbarProps.ts b/packages/tools/src/utilities/voi/widgets/colorbar/types/ColorbarProps.ts similarity index 59% rename from packages/core/src/ui/widgets/colorbar/types/ColorbarProps.ts rename to packages/tools/src/utilities/voi/widgets/colorbar/types/ColorbarProps.ts index 6324d381a4..750662b97f 100644 --- a/packages/core/src/ui/widgets/colorbar/types/ColorbarProps.ts +++ b/packages/tools/src/utilities/voi/widgets/colorbar/types/ColorbarProps.ts @@ -1,8 +1,8 @@ -import { Types } from '@cornerstonejs/core'; +import { IColorMapPreset } from '@kitware/vtk.js/Rendering/Core/ColorTransferFunction/ColorMaps'; import type { WidgetProps } from '../../types'; import { ColorbarCommonProps } from '.'; export type ColorbarProps = (WidgetProps & ColorbarCommonProps) & { - colormaps: Types.ColormapRegistration[]; + colormaps: IColorMapPreset[]; activeColormapName?: string; }; diff --git a/packages/core/src/ui/widgets/colorbar/types/ColorbarSize.ts b/packages/tools/src/utilities/voi/widgets/colorbar/types/ColorbarSize.ts similarity index 100% rename from packages/core/src/ui/widgets/colorbar/types/ColorbarSize.ts rename to packages/tools/src/utilities/voi/widgets/colorbar/types/ColorbarSize.ts diff --git a/packages/core/src/ui/widgets/colorbar/types/ColorbarTicksProps.ts b/packages/tools/src/utilities/voi/widgets/colorbar/types/ColorbarTicksProps.ts similarity index 100% rename from packages/core/src/ui/widgets/colorbar/types/ColorbarTicksProps.ts rename to packages/tools/src/utilities/voi/widgets/colorbar/types/ColorbarTicksProps.ts diff --git a/packages/core/src/ui/widgets/colorbar/types/ColorbarTicksStyle.ts b/packages/tools/src/utilities/voi/widgets/colorbar/types/ColorbarTicksStyle.ts similarity index 100% rename from packages/core/src/ui/widgets/colorbar/types/ColorbarTicksStyle.ts rename to packages/tools/src/utilities/voi/widgets/colorbar/types/ColorbarTicksStyle.ts diff --git a/packages/core/src/ui/widgets/colorbar/types/ColorbarVOIRange.ts b/packages/tools/src/utilities/voi/widgets/colorbar/types/ColorbarVOIRange.ts similarity index 100% rename from packages/core/src/ui/widgets/colorbar/types/ColorbarVOIRange.ts rename to packages/tools/src/utilities/voi/widgets/colorbar/types/ColorbarVOIRange.ts diff --git a/packages/core/src/ui/widgets/colorbar/types/ViewportColorbarProps.ts b/packages/tools/src/utilities/voi/widgets/colorbar/types/ViewportColorbarProps.ts similarity index 100% rename from packages/core/src/ui/widgets/colorbar/types/ViewportColorbarProps.ts rename to packages/tools/src/utilities/voi/widgets/colorbar/types/ViewportColorbarProps.ts diff --git a/packages/core/src/ui/widgets/colorbar/types/index.ts b/packages/tools/src/utilities/voi/widgets/colorbar/types/index.ts similarity index 100% rename from packages/core/src/ui/widgets/colorbar/types/index.ts rename to packages/tools/src/utilities/voi/widgets/colorbar/types/index.ts diff --git a/packages/core/src/ui/widgets/index.ts b/packages/tools/src/utilities/voi/widgets/index.ts similarity index 100% rename from packages/core/src/ui/widgets/index.ts rename to packages/tools/src/utilities/voi/widgets/index.ts diff --git a/packages/core/src/ui/widgets/types/WidgetProps.ts b/packages/tools/src/utilities/voi/widgets/types/WidgetProps.ts similarity index 100% rename from packages/core/src/ui/widgets/types/WidgetProps.ts rename to packages/tools/src/utilities/voi/widgets/types/WidgetProps.ts diff --git a/packages/core/src/ui/widgets/types/WidgetSize.ts b/packages/tools/src/utilities/voi/widgets/types/WidgetSize.ts similarity index 100% rename from packages/core/src/ui/widgets/types/WidgetSize.ts rename to packages/tools/src/utilities/voi/widgets/types/WidgetSize.ts diff --git a/packages/core/src/ui/widgets/types/index.ts b/packages/tools/src/utilities/voi/widgets/types/index.ts similarity index 100% rename from packages/core/src/ui/widgets/types/index.ts rename to packages/tools/src/utilities/voi/widgets/types/index.ts From 2124eec7d2b1580531e883a6a8c48f9dafd3da62 Mon Sep 17 00:00:00 2001 From: Leonardo Campos Date: Wed, 25 Oct 2023 12:09:48 -0300 Subject: [PATCH 10/14] moved 'widgets' to a different folder --- .../tools/src/utilities/voi/{widgets => }/colorbar/Colorbar.ts | 2 +- .../src/utilities/voi/{widgets => }/colorbar/ColorbarCanvas.ts | 2 +- .../src/utilities/voi/{widgets => }/colorbar/ColorbarTicks.ts | 0 .../utilities/voi/{widgets => }/colorbar/ViewportColorbar.ts | 2 +- .../voi/{widgets => }/colorbar/common/areColorbarRangesEqual.ts | 0 .../voi/{widgets => }/colorbar/common/areColorbarSizesEqual.ts | 0 .../src/utilities/voi/{widgets => }/colorbar/common/index.ts | 0 .../voi/{widgets => }/colorbar/common/isColorbarSizeValid.ts | 0 .../{widgets => }/colorbar/common/isRangeTextPositionValid.ts | 0 .../utilities/voi/{widgets => }/colorbar/common/isRangeValid.ts | 0 .../{widgets => }/colorbar/enums/ColorbarRangeTextPosition.ts | 0 .../src/utilities/voi/{widgets => }/colorbar/enums/index.ts | 0 .../tools/src/utilities/voi/{widgets => }/colorbar/index.ts | 0 .../voi/{widgets => }/colorbar/types/ColorbarCanvasProps.ts | 0 .../voi/{widgets => }/colorbar/types/ColorbarCommonProps.ts | 0 .../voi/{widgets => }/colorbar/types/ColorbarImageRange.ts | 0 .../utilities/voi/{widgets => }/colorbar/types/ColorbarProps.ts | 0 .../utilities/voi/{widgets => }/colorbar/types/ColorbarSize.ts | 0 .../voi/{widgets => }/colorbar/types/ColorbarTicksProps.ts | 0 .../voi/{widgets => }/colorbar/types/ColorbarTicksStyle.ts | 0 .../voi/{widgets => }/colorbar/types/ColorbarVOIRange.ts | 0 .../voi/{widgets => }/colorbar/types/ViewportColorbarProps.ts | 0 .../src/utilities/voi/{widgets => }/colorbar/types/index.ts | 0 packages/tools/src/utilities/voi/index.ts | 2 +- packages/tools/src/utilities/voi/widgets/index.ts | 1 - packages/tools/src/{utilities/voi => }/widgets/Widget.ts | 0 .../tools/src/{utilities/voi => }/widgets/types/WidgetProps.ts | 0 .../tools/src/{utilities/voi => }/widgets/types/WidgetSize.ts | 0 packages/tools/src/{utilities/voi => }/widgets/types/index.ts | 0 29 files changed, 4 insertions(+), 5 deletions(-) rename packages/tools/src/utilities/voi/{widgets => }/colorbar/Colorbar.ts (99%) rename packages/tools/src/utilities/voi/{widgets => }/colorbar/ColorbarCanvas.ts (99%) rename packages/tools/src/utilities/voi/{widgets => }/colorbar/ColorbarTicks.ts (100%) rename packages/tools/src/utilities/voi/{widgets => }/colorbar/ViewportColorbar.ts (98%) rename packages/tools/src/utilities/voi/{widgets => }/colorbar/common/areColorbarRangesEqual.ts (100%) rename packages/tools/src/utilities/voi/{widgets => }/colorbar/common/areColorbarSizesEqual.ts (100%) rename packages/tools/src/utilities/voi/{widgets => }/colorbar/common/index.ts (100%) rename packages/tools/src/utilities/voi/{widgets => }/colorbar/common/isColorbarSizeValid.ts (100%) rename packages/tools/src/utilities/voi/{widgets => }/colorbar/common/isRangeTextPositionValid.ts (100%) rename packages/tools/src/utilities/voi/{widgets => }/colorbar/common/isRangeValid.ts (100%) rename packages/tools/src/utilities/voi/{widgets => }/colorbar/enums/ColorbarRangeTextPosition.ts (100%) rename packages/tools/src/utilities/voi/{widgets => }/colorbar/enums/index.ts (100%) rename packages/tools/src/utilities/voi/{widgets => }/colorbar/index.ts (100%) rename packages/tools/src/utilities/voi/{widgets => }/colorbar/types/ColorbarCanvasProps.ts (100%) rename packages/tools/src/utilities/voi/{widgets => }/colorbar/types/ColorbarCommonProps.ts (100%) rename packages/tools/src/utilities/voi/{widgets => }/colorbar/types/ColorbarImageRange.ts (100%) rename packages/tools/src/utilities/voi/{widgets => }/colorbar/types/ColorbarProps.ts (100%) rename packages/tools/src/utilities/voi/{widgets => }/colorbar/types/ColorbarSize.ts (100%) rename packages/tools/src/utilities/voi/{widgets => }/colorbar/types/ColorbarTicksProps.ts (100%) rename packages/tools/src/utilities/voi/{widgets => }/colorbar/types/ColorbarTicksStyle.ts (100%) rename packages/tools/src/utilities/voi/{widgets => }/colorbar/types/ColorbarVOIRange.ts (100%) rename packages/tools/src/utilities/voi/{widgets => }/colorbar/types/ViewportColorbarProps.ts (100%) rename packages/tools/src/utilities/voi/{widgets => }/colorbar/types/index.ts (100%) delete mode 100644 packages/tools/src/utilities/voi/widgets/index.ts rename packages/tools/src/{utilities/voi => }/widgets/Widget.ts (100%) rename packages/tools/src/{utilities/voi => }/widgets/types/WidgetProps.ts (100%) rename packages/tools/src/{utilities/voi => }/widgets/types/WidgetSize.ts (100%) rename packages/tools/src/{utilities/voi => }/widgets/types/index.ts (100%) diff --git a/packages/tools/src/utilities/voi/widgets/colorbar/Colorbar.ts b/packages/tools/src/utilities/voi/colorbar/Colorbar.ts similarity index 99% rename from packages/tools/src/utilities/voi/widgets/colorbar/Colorbar.ts rename to packages/tools/src/utilities/voi/colorbar/Colorbar.ts index 92980a71c8..2d3ce3b92f 100644 --- a/packages/tools/src/utilities/voi/widgets/colorbar/Colorbar.ts +++ b/packages/tools/src/utilities/voi/colorbar/Colorbar.ts @@ -1,13 +1,13 @@ import { IColorMapPreset } from '@kitware/vtk.js/Rendering/Core/ColorTransferFunction/ColorMaps'; import { vec2 } from 'gl-matrix'; import { utilities as csUtils, Types } from '@cornerstonejs/core'; -import { Widget } from '../Widget'; import type { ColorbarProps, ColorbarVOIRange } from './types'; import { isRangeValid, areColorbarRangesEqual } from './common'; import { ColorbarRangeTextPosition } from './enums/ColorbarRangeTextPosition'; import { ColorbarCanvas } from './ColorbarCanvas'; import { ColorbarTicks } from './ColorbarTicks'; import isRangeTextPositionValid from './common/isRangeTextPositionValid'; +import Widget from '../../../widgets/Widget'; const DEFAULTS = { MULTIPLIER: 1, diff --git a/packages/tools/src/utilities/voi/widgets/colorbar/ColorbarCanvas.ts b/packages/tools/src/utilities/voi/colorbar/ColorbarCanvas.ts similarity index 99% rename from packages/tools/src/utilities/voi/widgets/colorbar/ColorbarCanvas.ts rename to packages/tools/src/utilities/voi/colorbar/ColorbarCanvas.ts index 9628acd4f5..2b7bde0018 100644 --- a/packages/tools/src/utilities/voi/widgets/colorbar/ColorbarCanvas.ts +++ b/packages/tools/src/utilities/voi/colorbar/ColorbarCanvas.ts @@ -1,6 +1,6 @@ import { IColorMapPreset } from '@kitware/vtk.js/Rendering/Core/ColorTransferFunction/ColorMaps'; import { utilities } from '@cornerstonejs/core'; -import interpolateVec3 from '../../../math/vec3/interpolateVec3'; +import interpolateVec3 from '../../math/vec3/interpolateVec3'; import { ColorbarCanvasProps } from './types/ColorbarCanvasProps'; import type { ColorbarImageRange, ColorbarVOIRange } from './types'; import type { ColorbarSize } from './types/ColorbarSize'; diff --git a/packages/tools/src/utilities/voi/widgets/colorbar/ColorbarTicks.ts b/packages/tools/src/utilities/voi/colorbar/ColorbarTicks.ts similarity index 100% rename from packages/tools/src/utilities/voi/widgets/colorbar/ColorbarTicks.ts rename to packages/tools/src/utilities/voi/colorbar/ColorbarTicks.ts diff --git a/packages/tools/src/utilities/voi/widgets/colorbar/ViewportColorbar.ts b/packages/tools/src/utilities/voi/colorbar/ViewportColorbar.ts similarity index 98% rename from packages/tools/src/utilities/voi/widgets/colorbar/ViewportColorbar.ts rename to packages/tools/src/utilities/voi/colorbar/ViewportColorbar.ts index c570a20c03..d9a857f1fc 100644 --- a/packages/tools/src/utilities/voi/widgets/colorbar/ViewportColorbar.ts +++ b/packages/tools/src/utilities/voi/colorbar/ViewportColorbar.ts @@ -9,7 +9,7 @@ import { } from '@cornerstonejs/core'; import { Colorbar } from './Colorbar'; import type { ViewportColorbarProps, ColorbarVOIRange } from './types'; -import { getVOIMultipliers } from '../../../getVOIMultipliers'; +import { getVOIMultipliers } from '../../getVOIMultipliers'; const { Events } = Enums; const defaultImageRange = { lower: -1000, upper: 1000 }; diff --git a/packages/tools/src/utilities/voi/widgets/colorbar/common/areColorbarRangesEqual.ts b/packages/tools/src/utilities/voi/colorbar/common/areColorbarRangesEqual.ts similarity index 100% rename from packages/tools/src/utilities/voi/widgets/colorbar/common/areColorbarRangesEqual.ts rename to packages/tools/src/utilities/voi/colorbar/common/areColorbarRangesEqual.ts diff --git a/packages/tools/src/utilities/voi/widgets/colorbar/common/areColorbarSizesEqual.ts b/packages/tools/src/utilities/voi/colorbar/common/areColorbarSizesEqual.ts similarity index 100% rename from packages/tools/src/utilities/voi/widgets/colorbar/common/areColorbarSizesEqual.ts rename to packages/tools/src/utilities/voi/colorbar/common/areColorbarSizesEqual.ts diff --git a/packages/tools/src/utilities/voi/widgets/colorbar/common/index.ts b/packages/tools/src/utilities/voi/colorbar/common/index.ts similarity index 100% rename from packages/tools/src/utilities/voi/widgets/colorbar/common/index.ts rename to packages/tools/src/utilities/voi/colorbar/common/index.ts diff --git a/packages/tools/src/utilities/voi/widgets/colorbar/common/isColorbarSizeValid.ts b/packages/tools/src/utilities/voi/colorbar/common/isColorbarSizeValid.ts similarity index 100% rename from packages/tools/src/utilities/voi/widgets/colorbar/common/isColorbarSizeValid.ts rename to packages/tools/src/utilities/voi/colorbar/common/isColorbarSizeValid.ts diff --git a/packages/tools/src/utilities/voi/widgets/colorbar/common/isRangeTextPositionValid.ts b/packages/tools/src/utilities/voi/colorbar/common/isRangeTextPositionValid.ts similarity index 100% rename from packages/tools/src/utilities/voi/widgets/colorbar/common/isRangeTextPositionValid.ts rename to packages/tools/src/utilities/voi/colorbar/common/isRangeTextPositionValid.ts diff --git a/packages/tools/src/utilities/voi/widgets/colorbar/common/isRangeValid.ts b/packages/tools/src/utilities/voi/colorbar/common/isRangeValid.ts similarity index 100% rename from packages/tools/src/utilities/voi/widgets/colorbar/common/isRangeValid.ts rename to packages/tools/src/utilities/voi/colorbar/common/isRangeValid.ts diff --git a/packages/tools/src/utilities/voi/widgets/colorbar/enums/ColorbarRangeTextPosition.ts b/packages/tools/src/utilities/voi/colorbar/enums/ColorbarRangeTextPosition.ts similarity index 100% rename from packages/tools/src/utilities/voi/widgets/colorbar/enums/ColorbarRangeTextPosition.ts rename to packages/tools/src/utilities/voi/colorbar/enums/ColorbarRangeTextPosition.ts diff --git a/packages/tools/src/utilities/voi/widgets/colorbar/enums/index.ts b/packages/tools/src/utilities/voi/colorbar/enums/index.ts similarity index 100% rename from packages/tools/src/utilities/voi/widgets/colorbar/enums/index.ts rename to packages/tools/src/utilities/voi/colorbar/enums/index.ts diff --git a/packages/tools/src/utilities/voi/widgets/colorbar/index.ts b/packages/tools/src/utilities/voi/colorbar/index.ts similarity index 100% rename from packages/tools/src/utilities/voi/widgets/colorbar/index.ts rename to packages/tools/src/utilities/voi/colorbar/index.ts diff --git a/packages/tools/src/utilities/voi/widgets/colorbar/types/ColorbarCanvasProps.ts b/packages/tools/src/utilities/voi/colorbar/types/ColorbarCanvasProps.ts similarity index 100% rename from packages/tools/src/utilities/voi/widgets/colorbar/types/ColorbarCanvasProps.ts rename to packages/tools/src/utilities/voi/colorbar/types/ColorbarCanvasProps.ts diff --git a/packages/tools/src/utilities/voi/widgets/colorbar/types/ColorbarCommonProps.ts b/packages/tools/src/utilities/voi/colorbar/types/ColorbarCommonProps.ts similarity index 100% rename from packages/tools/src/utilities/voi/widgets/colorbar/types/ColorbarCommonProps.ts rename to packages/tools/src/utilities/voi/colorbar/types/ColorbarCommonProps.ts diff --git a/packages/tools/src/utilities/voi/widgets/colorbar/types/ColorbarImageRange.ts b/packages/tools/src/utilities/voi/colorbar/types/ColorbarImageRange.ts similarity index 100% rename from packages/tools/src/utilities/voi/widgets/colorbar/types/ColorbarImageRange.ts rename to packages/tools/src/utilities/voi/colorbar/types/ColorbarImageRange.ts diff --git a/packages/tools/src/utilities/voi/widgets/colorbar/types/ColorbarProps.ts b/packages/tools/src/utilities/voi/colorbar/types/ColorbarProps.ts similarity index 100% rename from packages/tools/src/utilities/voi/widgets/colorbar/types/ColorbarProps.ts rename to packages/tools/src/utilities/voi/colorbar/types/ColorbarProps.ts diff --git a/packages/tools/src/utilities/voi/widgets/colorbar/types/ColorbarSize.ts b/packages/tools/src/utilities/voi/colorbar/types/ColorbarSize.ts similarity index 100% rename from packages/tools/src/utilities/voi/widgets/colorbar/types/ColorbarSize.ts rename to packages/tools/src/utilities/voi/colorbar/types/ColorbarSize.ts diff --git a/packages/tools/src/utilities/voi/widgets/colorbar/types/ColorbarTicksProps.ts b/packages/tools/src/utilities/voi/colorbar/types/ColorbarTicksProps.ts similarity index 100% rename from packages/tools/src/utilities/voi/widgets/colorbar/types/ColorbarTicksProps.ts rename to packages/tools/src/utilities/voi/colorbar/types/ColorbarTicksProps.ts diff --git a/packages/tools/src/utilities/voi/widgets/colorbar/types/ColorbarTicksStyle.ts b/packages/tools/src/utilities/voi/colorbar/types/ColorbarTicksStyle.ts similarity index 100% rename from packages/tools/src/utilities/voi/widgets/colorbar/types/ColorbarTicksStyle.ts rename to packages/tools/src/utilities/voi/colorbar/types/ColorbarTicksStyle.ts diff --git a/packages/tools/src/utilities/voi/widgets/colorbar/types/ColorbarVOIRange.ts b/packages/tools/src/utilities/voi/colorbar/types/ColorbarVOIRange.ts similarity index 100% rename from packages/tools/src/utilities/voi/widgets/colorbar/types/ColorbarVOIRange.ts rename to packages/tools/src/utilities/voi/colorbar/types/ColorbarVOIRange.ts diff --git a/packages/tools/src/utilities/voi/widgets/colorbar/types/ViewportColorbarProps.ts b/packages/tools/src/utilities/voi/colorbar/types/ViewportColorbarProps.ts similarity index 100% rename from packages/tools/src/utilities/voi/widgets/colorbar/types/ViewportColorbarProps.ts rename to packages/tools/src/utilities/voi/colorbar/types/ViewportColorbarProps.ts diff --git a/packages/tools/src/utilities/voi/widgets/colorbar/types/index.ts b/packages/tools/src/utilities/voi/colorbar/types/index.ts similarity index 100% rename from packages/tools/src/utilities/voi/widgets/colorbar/types/index.ts rename to packages/tools/src/utilities/voi/colorbar/types/index.ts diff --git a/packages/tools/src/utilities/voi/index.ts b/packages/tools/src/utilities/voi/index.ts index 31db82c42d..381f2c4f09 100644 --- a/packages/tools/src/utilities/voi/index.ts +++ b/packages/tools/src/utilities/voi/index.ts @@ -1 +1 @@ -export { colorbar } from './widgets'; +export * as colorbar from './colorbar'; diff --git a/packages/tools/src/utilities/voi/widgets/index.ts b/packages/tools/src/utilities/voi/widgets/index.ts deleted file mode 100644 index 381f2c4f09..0000000000 --- a/packages/tools/src/utilities/voi/widgets/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * as colorbar from './colorbar'; diff --git a/packages/tools/src/utilities/voi/widgets/Widget.ts b/packages/tools/src/widgets/Widget.ts similarity index 100% rename from packages/tools/src/utilities/voi/widgets/Widget.ts rename to packages/tools/src/widgets/Widget.ts diff --git a/packages/tools/src/utilities/voi/widgets/types/WidgetProps.ts b/packages/tools/src/widgets/types/WidgetProps.ts similarity index 100% rename from packages/tools/src/utilities/voi/widgets/types/WidgetProps.ts rename to packages/tools/src/widgets/types/WidgetProps.ts diff --git a/packages/tools/src/utilities/voi/widgets/types/WidgetSize.ts b/packages/tools/src/widgets/types/WidgetSize.ts similarity index 100% rename from packages/tools/src/utilities/voi/widgets/types/WidgetSize.ts rename to packages/tools/src/widgets/types/WidgetSize.ts diff --git a/packages/tools/src/utilities/voi/widgets/types/index.ts b/packages/tools/src/widgets/types/index.ts similarity index 100% rename from packages/tools/src/utilities/voi/widgets/types/index.ts rename to packages/tools/src/widgets/types/index.ts From ebb859067a82586f4154cda78f9bc548fa20cff4 Mon Sep 17 00:00:00 2001 From: Leonardo Campos Date: Wed, 25 Oct 2023 12:12:06 -0300 Subject: [PATCH 11/14] example :: renamed 'Color Bar' to 'colorbar' --- packages/tools/examples/colorbar/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/tools/examples/colorbar/index.ts b/packages/tools/examples/colorbar/index.ts index 7459932926..5ae52ef116 100644 --- a/packages/tools/examples/colorbar/index.ts +++ b/packages/tools/examples/colorbar/index.ts @@ -122,8 +122,8 @@ const viewportsInfo = [ // ======== Set up page ======== // setTitleAndDescription( - 'Color Bar', - 'Interactive color bar that can be used to manipulate the VOI' + 'Colorbar', + 'Interactive colorbar that can be used to manipulate the VOI' ); const content = document.getElementById('content'); From b45a1e15b950ca8033e8a056167fee8329154b02 Mon Sep 17 00:00:00 2001 From: Leonardo Campos Date: Wed, 25 Oct 2023 14:25:45 -0300 Subject: [PATCH 12/14] updated colorbar example for volume and stack viewports --- packages/tools/examples/colorbar/index.ts | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/packages/tools/examples/colorbar/index.ts b/packages/tools/examples/colorbar/index.ts index 5ae52ef116..5deecb3e44 100644 --- a/packages/tools/examples/colorbar/index.ts +++ b/packages/tools/examples/colorbar/index.ts @@ -337,8 +337,8 @@ function initializeColorbars(viewportInfo, colorbarContainers) { const { fusion, volumeIds, colorbar, viewportInput } = viewportInfo; const { element } = viewportInput; - // Stack viewports do not have volumeIds - const ctVolumeId = volumeIds?.length ? volumeIds[0] : undefined; + // Volume viewport has one or two volumeIds while stack viewport does not have + const isVolumeViewport = !!volumeIds?.length; const scaleStyle = { font: '12px Arial', @@ -349,18 +349,24 @@ function initializeColorbars(viewportInfo, colorbarContainers) { labelMargin: 3, }; - const ctColorbar = new ViewportColorbar({ + const ctColorbarProps: cstUtils.voi.colorbar.Types.ViewportColorbarProps = { id: 'ctColorbar', element, container: colorbarContainers[0], - volumeId: ctVolumeId, colormaps, activeColormapName: 'Grayscale', ticks: { position: ColorbarRangeTextPosition.Left, style: scaleStyle, }, - }); + }; + + // Colorbars on volume viewports must have a volumeId + if (isVolumeViewport) { + ctColorbarProps.volumeId = volumeIds[0]; + } + + const ctColorbar = new ViewportColorbar(ctColorbarProps); colorbar.instances.push(ctColorbar); From 6d204febe14aa8837cd5dc6414de1ee32221e71f Mon Sep 17 00:00:00 2001 From: Leonardo Campos Date: Wed, 25 Oct 2023 15:18:57 -0300 Subject: [PATCH 13/14] fixed build --- packages/core/src/types/index.ts | 3 ++- .../tools/src/utilities/voi/colorbar/ViewportColorbar.ts | 8 ++++---- packages/tools/src/utilities/voi/colorbar/index.ts | 7 +++++-- .../src/utilities/voi/colorbar/types/ColorbarProps.ts | 2 +- packages/tools/src/utilities/voi/index.ts | 4 +++- 5 files changed, 15 insertions(+), 9 deletions(-) diff --git a/packages/core/src/types/index.ts b/packages/core/src/types/index.ts index 60510e273f..1afeaabf60 100644 --- a/packages/core/src/types/index.ts +++ b/packages/core/src/types/index.ts @@ -14,7 +14,7 @@ import type IRegisterImageLoader from './IRegisterImageLoader'; import type IStreamingVolumeProperties from './IStreamingVolumeProperties'; import type CustomEventType from './CustomEventType'; import type { IViewport, PublicViewportInput } from './IViewport'; -import type { VolumeActor, Actor, ActorEntry } from './IActor'; +import type { VolumeActor, Actor, ActorEntry, ImageActor } from './IActor'; import type { IImageLoadObject, IVolumeLoadObject, @@ -121,6 +121,7 @@ export type { VolumeActor, Actor, ActorEntry, + ImageActor, IImageLoadObject, IVolumeLoadObject, IVolumeInput, diff --git a/packages/tools/src/utilities/voi/colorbar/ViewportColorbar.ts b/packages/tools/src/utilities/voi/colorbar/ViewportColorbar.ts index d9a857f1fc..54639e7d3f 100644 --- a/packages/tools/src/utilities/voi/colorbar/ViewportColorbar.ts +++ b/packages/tools/src/utilities/voi/colorbar/ViewportColorbar.ts @@ -96,15 +96,15 @@ class ViewportColorbar extends Colorbar { const enabledElement = getEnabledElement(element); const { viewport } = enabledElement; - const actor = volumeId + const volumeActor = volumeId ? viewport.getActor(volumeId) : viewport.getDefaultActor(); - if (!actor || !utilities.isImageActor(actor)) { + if (!volumeActor || !utilities.isImageActor(volumeActor)) { return defaultImageRange; } - const voiRange = actor.actor + const voiRange = (volumeActor.actor as Types.ImageActor) .getProperty() .getRGBTransferFunction(0) .getRange(); @@ -186,7 +186,7 @@ class ViewportColorbar extends Colorbar { element.addEventListener( Events.VOI_MODIFIED, - this._viewportVOIModifiedCallback + this._viewportVOIModifiedCallback as EventListener ); } } diff --git a/packages/tools/src/utilities/voi/colorbar/index.ts b/packages/tools/src/utilities/voi/colorbar/index.ts index a724b78ddd..0ba87a2a0c 100644 --- a/packages/tools/src/utilities/voi/colorbar/index.ts +++ b/packages/tools/src/utilities/voi/colorbar/index.ts @@ -1,5 +1,8 @@ -export * as Enums from './enums'; -export type * as Types from './types'; +import * as Enums from './enums'; +import type * as Types from './types'; + +export type { Types }; +export { Enums }; export { Colorbar } from './Colorbar'; export { ViewportColorbar } from './ViewportColorbar'; diff --git a/packages/tools/src/utilities/voi/colorbar/types/ColorbarProps.ts b/packages/tools/src/utilities/voi/colorbar/types/ColorbarProps.ts index 750662b97f..52fcb4b674 100644 --- a/packages/tools/src/utilities/voi/colorbar/types/ColorbarProps.ts +++ b/packages/tools/src/utilities/voi/colorbar/types/ColorbarProps.ts @@ -1,5 +1,5 @@ import { IColorMapPreset } from '@kitware/vtk.js/Rendering/Core/ColorTransferFunction/ColorMaps'; -import type { WidgetProps } from '../../types'; +import { WidgetProps } from '../../../../widgets/types'; import { ColorbarCommonProps } from '.'; export type ColorbarProps = (WidgetProps & ColorbarCommonProps) & { diff --git a/packages/tools/src/utilities/voi/index.ts b/packages/tools/src/utilities/voi/index.ts index 381f2c4f09..ac7d1357fb 100644 --- a/packages/tools/src/utilities/voi/index.ts +++ b/packages/tools/src/utilities/voi/index.ts @@ -1 +1,3 @@ -export * as colorbar from './colorbar'; +import * as colorbar from './colorbar'; + +export { colorbar }; From 7760fecef7858d80f25e9aa2add0958f9e9aefe2 Mon Sep 17 00:00:00 2001 From: Leonardo Campos Date: Wed, 25 Oct 2023 15:40:56 -0300 Subject: [PATCH 14/14] update-api --- common/reviews/api/core.api.md | 43 + common/reviews/api/nifti-volume-loader.api.md | 3259 +++++++++-------- .../api/streaming-image-volume-loader.api.md | 3 + common/reviews/api/tools.api.md | 155 +- 4 files changed, 1831 insertions(+), 1629 deletions(-) diff --git a/common/reviews/api/core.api.md b/common/reviews/api/core.api.md index b29695fd90..b4ec395d93 100644 --- a/common/reviews/api/core.api.md +++ b/common/reviews/api/core.api.md @@ -209,6 +209,9 @@ function cancelLoadImage(imageId: string): void; // @public (undocumented) function cancelLoadImages(imageIds: Array): void; +// @public (undocumented) +function clamp(value: number, min: number, max: number): number; + declare namespace colormap { export { getColormap, @@ -644,6 +647,13 @@ export { Enums } // @public (undocumented) const EPSILON = 0.001; +declare namespace eventListener { + export { + TargetEventListeners, + MultiTargetEventListenerManager + } +} + // @public (undocumented) export enum EVENTS { // (undocumented) @@ -868,6 +878,9 @@ function getTransferFunctionNodes(transferFunction: any): any[]; // @public (undocumented) function getViewportImageCornersInWorld(viewport: IStackViewport | IVolumeViewport): Point3[]; +// @public (undocumented) +function getViewportModality(viewport: IViewport, volumeId?: string): string; + // @public (undocumented) function getViewportsWithImageURI(imageURI: string, renderingEngineId?: string): Array; @@ -1332,6 +1345,9 @@ interface IImageVolume { vtkOpenGLTexture: any; } +// @public (undocumented) +type ImageActor = vtkImageSlice; + // @public (undocumented) type ImageCacheImageAddedEvent = CustomEvent_2; @@ -2040,6 +2056,16 @@ const metadataProvider: { // @public (undocumented) const mprCameraValues: any; +// @public (undocumented) +class MultiTargetEventListenerManager { + // (undocumented) + addEventListener(target: EventTarget, type: string, callback: EventListener, options?: AddEventListenerOptions): void; + // (undocumented) + removeEventListener(target: EventTarget, type: string, callback?: EventListener, options?: EventListenerOptions): void; + // (undocumented) + reset(): void; +} + // @public (undocumented) enum OrientationAxis { // (undocumented) @@ -2460,6 +2486,19 @@ type SurfaceData = { polys: number[]; }; +// @public (undocumented) +class TargetEventListeners { + constructor(target: EventTarget); + // (undocumented) + addEventListener(type: string, callback: EventListener, options?: AddEventListenerOptions): void; + // (undocumented) + get isEmpty(): boolean; + // (undocumented) + removeEventListener(type: string, callback?: EventListener, options?: EventListenerOptions): void; + // (undocumented) + reset(): void; +} + // @public (undocumented) function threePlaneIntersection(firstPlane: Plane, secondPlane: Plane, thirdPlane: Plane): Point3; @@ -2527,6 +2566,7 @@ declare namespace Types { VolumeActor, Actor, ActorEntry, + ImageActor, IImageLoadObject, IVolumeLoadObject, IVolumeInput, @@ -2586,6 +2626,7 @@ function unregisterAllImageLoaders(): void; declare namespace utilities { export { + eventListener, invertRgbTransferFunction, createSigmoidRGBTransferFunction, getVoiFromSigmoidRGBTransferFunction, @@ -2594,6 +2635,7 @@ declare namespace utilities { triggerEvent, imageIdToURI, metadataProvider as calibratedPixelSpacingMetadataProvider, + clamp, uuidv4, planar, getMinMax, @@ -2604,6 +2646,7 @@ declare namespace utilities { createUint8SharedArray, createUint16SharedArray, createInt16SharedArray, + getViewportModality, windowLevel, getClosestImageId, getSpacingInNormalDirection, diff --git a/common/reviews/api/nifti-volume-loader.api.md b/common/reviews/api/nifti-volume-loader.api.md index c52e02d12c..7b540d84f6 100644 --- a/common/reviews/api/nifti-volume-loader.api.md +++ b/common/reviews/api/nifti-volume-loader.api.md @@ -1,1628 +1,1631 @@ -## API Report File for "@cornerstonejs/nifti-volume-loader" - -> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). - -```ts - -import type { GetGPUTier } from 'detect-gpu'; -import type { mat4 } from 'gl-matrix'; -import type { TierResult } from 'detect-gpu'; -import type vtkActor from '@kitware/vtk.js/Rendering/Core/Actor'; -import type { vtkImageData } from '@kitware/vtk.js/Common/DataModel/ImageData'; -import vtkImageSlice from '@kitware/vtk.js/Rendering/Core/ImageSlice'; -import type vtkVolume from '@kitware/vtk.js/Rendering/Core/Volume'; - -// @public (undocumented) -type Actor = vtkActor; - -// @public -type ActorEntry = { - uid: string; - actor: Actor | VolumeActor | ImageActor; - referenceId?: string; - slabThickness?: number; - clippingFilter?: any; -}; - -// @public -type ActorSliceRange = { - actor: VolumeActor; - viewPlaneNormal: Point3; - focalPoint: Point3; - min: number; - max: number; - current: number; -}; - -// @public (undocumented) -type AffineMatrix = [ -[number, number, number, number], -[number, number, number, number], -[number, number, number, number], -[number, number, number, number] -]; - -// @public -type CameraModifiedEvent = CustomEvent_2; - -// @public -type CameraModifiedEventDetail = { - previousCamera: ICamera; - camera: ICamera; - element: HTMLDivElement; - viewportId: string; - renderingEngineId: string; - rotation?: number; -}; - -// @public (undocumented) -type ColormapPublic = { - name?: string; - opacity?: OpacityMapping[] | number; - /** midpoint mapping between values to opacity if the colormap - * is getting used for fusion, this is an array of arrays which - * each array containing 2 values, the first value is the value - * to map to opacity and the second value is the opacity value. - * By default, the minimum value is mapped to 0 opacity and the - * maximum value is mapped to 1 opacity, but you can configure - * the points in the middle to be mapped to different opacities - * instead of a linear mapping from 0 to 1. - */ -}; - -// @public (undocumented) -type ColormapRegistration = { - ColorSpace: string; - Name: string; - RGBPoints: RGB[]; -}; - -// @public (undocumented) -type ContourData = { - points: Point3[]; - type: ContourType; - color: Point3; - segmentIndex: number; -}; - -// @public (undocumented) -type ContourSetData = { - id: string; - data: ContourData[]; - frameOfReferenceUID: string; - color?: Point3; - segmentIndex?: number; -}; - -// @public (undocumented) -type Cornerstone3DConfig = { - gpuTier?: TierResult; - detectGPUConfig: GetGPUTier; - rendering: { - // vtk.js supports 8bit integer textures and 32bit float textures. - // However, if the client has norm16 textures (it can be seen by visiting - // the webGl report at https://webglreport.com/?v=2), vtk will be default - // to use it to improve memory usage. However, if the client don't have - // it still another level of optimization can happen by setting the - // preferSizeOverAccuracy since it will reduce the size of the texture to half - // float at the cost of accuracy in rendering. This is a tradeoff that the - // client can decide. - // - // Read more in the following Pull Request: - // 1. HalfFloat: https://github.com/Kitware/vtk-js/pull/2046 - // 2. Norm16: https://github.com/Kitware/vtk-js/pull/2058 - preferSizeOverAccuracy: boolean; - // Whether the EXT_texture_norm16 extension is supported by the browser. - // WebGL 2 report (link: https://webglreport.com/?v=2) can be used to check - // if the browser supports this extension. - // In case the browser supports this extension, instead of using 32bit float - // textures, 16bit float textures will be used to reduce the memory usage where - // possible. - // Norm16 may not work currently due to the two active bugs in chrome + safari - // https://bugs.chromium.org/p/chromium/issues/detail?id=1408247 - // https://bugs.webkit.org/show_bug.cgi?id=252039 - useNorm16Texture: boolean; - useCPURendering: boolean; - strictZSpacingForVolumeViewport: boolean; - }; -}; - -// @public (undocumented) -export function cornerstoneNiftiImageVolumeLoader(volumeId: string): IVolumeLoader; - -// @public (undocumented) -interface CPUFallbackColormap { - // (undocumented) - addColor: (rgba: Point4) => void; - // (undocumented) - buildLookupTable: (lut: CPUFallbackLookupTable) => void; - // (undocumented) - clearColors: () => void; - // (undocumented) - createLookupTable: () => CPUFallbackLookupTable; - // (undocumented) - getColor: (index: number) => Point4; - // (undocumented) - getColorRepeating: (index: number) => Point4; - // (undocumented) - getColorSchemeName: () => string; - getId: () => string; - // (undocumented) - getNumberOfColors: () => number; - // (undocumented) - insertColor: (index: number, rgba: Point4) => void; - // (undocumented) - isValidIndex: (index: number) => boolean; - // (undocumented) - removeColor: (index: number) => void; - // (undocumented) - setColor: (index: number, rgba: Point4) => void; - // (undocumented) - setColorSchemeName: (name: string) => void; - // (undocumented) - setNumberOfColors: (numColors: number) => void; -} - -// @public (undocumented) -type CPUFallbackColormapData = { - name: string; - numOfColors?: number; - colors?: Point4[]; - segmentedData?: unknown; - numColors?: number; - gamma?: number; -}; - -// @public (undocumented) -type CPUFallbackColormapsData = { - [key: string]: CPUFallbackColormapData; -}; - -// @public (undocumented) -interface CPUFallbackEnabledElement { - // (undocumented) - canvas?: HTMLCanvasElement; - // (undocumented) - colormap?: CPUFallbackColormap; - // (undocumented) - image?: IImage; - // (undocumented) - invalid?: boolean; - // (undocumented) - metadata?: { - direction?: Mat3; - dimensions?: Point3; - spacing?: Point3; - origin?: Point3; - imagePlaneModule?: ImagePlaneModule; - imagePixelModule?: ImagePixelModule; - }; - // (undocumented) - needsRedraw?: boolean; - // (undocumented) - options?: { - [key: string]: unknown; - colormap?: CPUFallbackColormap; - }; - // (undocumented) - pan?: Point2; - // (undocumented) - renderingTools?: CPUFallbackRenderingTools; - // (undocumented) - rotation?: number; - // (undocumented) - scale?: number; - // (undocumented) - transform?: CPUFallbackTransform; - // (undocumented) - viewport?: CPUFallbackViewport; - // (undocumented) - zoom?: number; -} - -// @public (undocumented) -interface CPUFallbackLookupTable { - // (undocumented) - build: (force: boolean) => void; - // (undocumented) - getColor: (scalar: number) => Point4; - // (undocumented) - setAlphaRange: (start: number, end: number) => void; - // (undocumented) - setHueRange: (start: number, end: number) => void; - // (undocumented) - setNumberOfTableValues: (number: number) => void; - // (undocumented) - setRamp: (ramp: string) => void; - // (undocumented) - setRange: (start: number, end: number) => void; - // (undocumented) - setSaturationRange: (start: number, end: number) => void; - // (undocumented) - setTableRange: (start: number, end: number) => void; - // (undocumented) - setTableValue(index: number, rgba: Point4); - // (undocumented) - setValueRange: (start: number, end: number) => void; -} - -// @public (undocumented) -type CPUFallbackLUT = { - lut: number[]; -}; - -// @public (undocumented) -type CPUFallbackRenderingTools = { - renderCanvas?: HTMLCanvasElement; - lastRenderedIsColor?: boolean; - lastRenderedImageId?: string; - lastRenderedViewport?: { - windowWidth: number | number[]; - windowCenter: number | number[]; - invert: boolean; - rotation: number; - hflip: boolean; - vflip: boolean; - modalityLUT: CPUFallbackLUT; - voiLUT: CPUFallbackLUT; - colormap: unknown; - }; - renderCanvasContext?: CanvasRenderingContext2D; - colormapId?: string; - colorLUT?: CPUFallbackLookupTable; - renderCanvasData?: ImageData; -}; - -// @public (undocumented) -interface CPUFallbackTransform { - // (undocumented) - clone: () => CPUFallbackTransform; - // (undocumented) - getMatrix: () => TransformMatrix2D; - // (undocumented) - invert: () => void; - // (undocumented) - multiply: (matrix: TransformMatrix2D) => void; - // (undocumented) - reset: () => void; - // (undocumented) - rotate: (rad: number) => void; - // (undocumented) - scale: (sx: number, sy: number) => void; - // (undocumented) - transformPoint: (point: Point2) => Point2; - // (undocumented) - translate: (x: number, y: number) => void; -} - -// @public (undocumented) -type CPUFallbackViewport = { - scale?: number; - parallelScale?: number; - focalPoint?: number[]; - translation?: { - x: number; - y: number; - }; - voi?: { - windowWidth: number; - windowCenter: number; - }; - invert?: boolean; - pixelReplication?: boolean; - rotation?: number; - hflip?: boolean; - vflip?: boolean; - modalityLUT?: CPUFallbackLUT; - voiLUT?: CPUFallbackLUT; - colormap?: CPUFallbackColormap; - displayedArea?: CPUFallbackViewportDisplayedArea; - modality?: string; -}; - -// @public (undocumented) -type CPUFallbackViewportDisplayedArea = { - tlhc: { - x: number; - y: number; - }; - brhc: { - x: number; - y: number; - }; - rowPixelSpacing: number; - columnPixelSpacing: number; - presentationSizeMode: string; -}; - -// @public (undocumented) -type CPUIImageData = { - dimensions: Point3; - direction: Mat3; - spacing: Point3; - origin: Point3; - imageData: CPUImageData; - metadata: { Modality: string }; - scalarData: PixelDataTypedArray; - scaling: Scaling; - hasPixelSpacing?: boolean; - calibration?: IImageCalibration; - - preScale?: { - scaled?: boolean; - scalingParameters?: { - modality?: string; - rescaleSlope?: number; - rescaleIntercept?: number; - suvbw?: number; - }; - }; -}; - -// @public (undocumented) -type CPUImageData = { - worldToIndex?: (point: Point3) => Point3; - indexToWorld?: (point: Point3) => Point3; - getWorldToIndex?: () => Point3; - getIndexToWorld?: () => Point3; - getSpacing?: () => Point3; - getDirection?: () => Mat3; - getScalarData?: () => PixelDataTypedArray; - getDimensions?: () => Point3; -}; - -// @public (undocumented) -interface CustomEvent_2 extends Event { - readonly detail: T; - // (undocumented) - initCustomEvent( - typeArg: string, - canBubbleArg: boolean, - cancelableArg: boolean, - detailArg: T - ): void; -} - -// @public (undocumented) -type DisplayArea = { - imageArea: [number, number]; // areaX, areaY - imageCanvasPoint: { - imagePoint: [number, number]; // imageX, imageY - canvasPoint: [number, number]; // canvasX, canvasY - }; - storeAsInitialCamera: boolean; -}; - -// @public -type DisplayAreaModifiedEvent = CustomEvent_2; - -// @public -type DisplayAreaModifiedEventDetail = { - viewportId: string; - displayArea: DisplayArea; - volumeId?: string; - storeAsInitialCamera?: boolean; -}; - -// @public -type ElementDisabledEvent = CustomEvent_2; - -// @public -type ElementDisabledEventDetail = { - element: HTMLDivElement; - viewportId: string; - renderingEngineId: string; -}; - -// @public -type ElementEnabledEvent = CustomEvent_2; - -// @public -type ElementEnabledEventDetail = { - element: HTMLDivElement; - viewportId: string; - renderingEngineId: string; -}; - -declare namespace Enums { - export { - Events - } -} -export { Enums } - -// @public (undocumented) -enum Events { - // (undocumented) - NIFTI_VOLUME_LOADED = "CORNERSTONE_NIFTI_VOLUME_LOADED", - // (undocumented) - NIFTI_VOLUME_PROGRESS = "CORNERSTONE_NIFTI_VOLUME_PROGRESS" -} - -declare namespace EventTypes { - export { - CameraModifiedEventDetail, - CameraModifiedEvent, - VoiModifiedEvent, - VoiModifiedEventDetail, - DisplayAreaModifiedEvent, - DisplayAreaModifiedEventDetail, - ElementDisabledEvent, - ElementDisabledEventDetail, - ElementEnabledEvent, - ElementEnabledEventDetail, - ImageRenderedEventDetail, - ImageRenderedEvent, - ImageVolumeModifiedEvent, - ImageVolumeModifiedEventDetail, - ImageVolumeLoadingCompletedEvent, - ImageVolumeLoadingCompletedEventDetail, - ImageLoadedEvent, - ImageLoadedEventDetail, - ImageLoadedFailedEventDetail, - ImageLoadedFailedEvent, - VolumeLoadedEvent, - VolumeLoadedEventDetail, - VolumeLoadedFailedEvent, - VolumeLoadedFailedEventDetail, - ImageCacheImageAddedEvent, - ImageCacheImageAddedEventDetail, - ImageCacheImageRemovedEvent, - ImageCacheImageRemovedEventDetail, - VolumeCacheVolumeAddedEvent, - VolumeCacheVolumeAddedEventDetail, - VolumeCacheVolumeRemovedEvent, - VolumeCacheVolumeRemovedEventDetail, - StackNewImageEvent, - StackNewImageEventDetail, - PreStackNewImageEvent, - PreStackNewImageEventDetail, - ImageSpacingCalibratedEvent, - ImageSpacingCalibratedEventDetail, - ImageLoadProgressEvent, - ImageLoadProgressEventDetail, - VolumeNewImageEvent, - VolumeNewImageEventDetail, - StackViewportNewStackEvent, - StackViewportNewStackEventDetail, - StackViewportScrollEvent, - StackViewportScrollEventDetail - } -} - -// @public (undocumented) -function fetchAndAllocateNiftiVolume(volumeId: string): Promise; - -// @public -type FlipDirection = { - flipHorizontal?: boolean; - flipVertical?: boolean; -}; - -declare namespace helpers { - export { - modalityScaleNifti, - makeVolumeMetadata, - fetchAndAllocateNiftiVolume - } -} -export { helpers } - -// @public (undocumented) -interface ICache { - getCacheSize: () => number; - getImageLoadObject: (imageId: string) => IImageLoadObject | void; - getMaxCacheSize: () => number; - getVolumeLoadObject: (volumeId: string) => IVolumeLoadObject | void; - purgeCache: () => void; - putImageLoadObject: ( - imageId: string, - imageLoadObject: IImageLoadObject - ) => Promise; - putVolumeLoadObject: ( - volumeId: string, - volumeLoadObject: IVolumeLoadObject - ) => Promise; - setMaxCacheSize: (maxCacheSize: number) => void; -} - -// @public (undocumented) -interface ICachedGeometry { - // (undocumented) - geometry?: IGeometry; - // (undocumented) - geometryId: string; - // (undocumented) - geometryLoadObject: IGeometryLoadObject; - // (undocumented) - loaded: boolean; - // (undocumented) - sizeInBytes: number; - // (undocumented) - timeStamp: number; -} - -// @public (undocumented) -interface ICachedImage { - // (undocumented) - image?: IImage; - // (undocumented) - imageId: string; - // (undocumented) - imageLoadObject: IImageLoadObject; - // (undocumented) - loaded: boolean; - // (undocumented) - sharedCacheKey?: string; - // (undocumented) - sizeInBytes: number; - // (undocumented) - timeStamp: number; -} - -// @public (undocumented) -interface ICachedVolume { - // (undocumented) - loaded: boolean; - // (undocumented) - sizeInBytes: number; - // (undocumented) - timeStamp: number; - // (undocumented) - volume?: IImageVolume; - // (undocumented) - volumeId: string; - // (undocumented) - volumeLoadObject: IVolumeLoadObject; -} - -// @public -interface ICamera { - clippingRange?: Point2; - flipHorizontal?: boolean; - flipVertical?: boolean; - focalPoint?: Point3; - parallelProjection?: boolean; - parallelScale?: number; - position?: Point3; - scale?: number; - viewAngle?: number; - viewPlaneNormal?: Point3; - viewUp?: Point3; -} - -// @public (undocumented) -interface IContour { - // (undocumented) - color: any; - // (undocumented) - getColor(): Point3; - // (undocumented) - getFlatPointsArray(): number[]; - getPoints(): Point3[]; - // (undocumented) - _getSizeInBytes(): number; - // (undocumented) - getType(): ContourType; - // (undocumented) - readonly id: string; - // (undocumented) - points: Point3[]; - // (undocumented) - readonly sizeInBytes: number; -} - -// @public -interface IContourSet { - // (undocumented) - contours: IContour[]; - // (undocumented) - _createEachContour(data: ContourData[]): void; - // (undocumented) - readonly frameOfReferenceUID: string; - // (undocumented) - getCentroid(): Point3; - // (undocumented) - getColor(): any; - getContours(): IContour[]; - getFlatPointsArray(): Point3[]; - getNumberOfContours(): number; - getNumberOfPointsArray(): number[]; - getNumberOfPointsInAContour(contourIndex: number): number; - getPointsInContour(contourIndex: number): Point3[]; - // (undocumented) - getSegmentIndex(): number; - // (undocumented) - getSizeInBytes(): number; - getTotalNumberOfPoints(): number; - // (undocumented) - readonly id: string; - // (undocumented) - readonly sizeInBytes: number; -} - -// @public -interface IDynamicImageVolume extends IImageVolume { - getScalarDataArrays(): VolumeScalarData[]; - get numTimePoints(): number; - get timePointIndex(): number; - set timePointIndex(newTimePointIndex: number); -} - -// @public -interface IEnabledElement { - FrameOfReferenceUID: string; - renderingEngine: IRenderingEngine; - renderingEngineId: string; - viewport: IStackViewport | IVolumeViewport; - viewportId: string; -} - -// @public (undocumented) -interface IGeometry { - // (undocumented) - data: IContourSet | Surface; - // (undocumented) - id: string; - // (undocumented) - sizeInBytes: number; - // (undocumented) - type: GeometryType; -} - -// @public (undocumented) -interface IGeometryLoadObject { - cancelFn?: () => void; - decache?: () => void; - promise: Promise; -} - -// @public -interface IImage { - cachedLut?: { - windowWidth?: number | number[]; - windowCenter?: number | number[]; - invert?: boolean; - lutArray?: Uint8ClampedArray; - modalityLUT?: unknown; - voiLUT?: CPUFallbackLUT; - }; - color: boolean; - colormap?: CPUFallbackColormap; - columnPixelSpacing: number; - columns: number; - // (undocumented) - decodeTimeInMS?: number; - // (undocumented) - getCanvas: () => HTMLCanvasElement; - getPixelData: () => PixelDataTypedArray; - height: number; - imageId: string; - intercept: number; - invert: boolean; - isPreScaled?: boolean; - // (undocumented) - loadTimeInMS?: number; - // (undocumented) - maxPixelValue: number; - minPixelValue: number; - modalityLUT?: CPUFallbackLUT; - numComps: number; - photometricInterpretation?: string; - preScale?: { - scaled?: boolean; - scalingParameters?: { - modality?: string; - rescaleSlope?: number; - rescaleIntercept?: number; - suvbw?: number; - }; - }; - render?: ( - enabledElement: CPUFallbackEnabledElement, - invalidated: boolean - ) => unknown; - rgba: boolean; - rowPixelSpacing: number; - rows: number; - scaling?: { - PT?: { - // @TODO: Do these values exist? - SUVlbmFactor?: number; - SUVbsaFactor?: number; - // accessed in ProbeTool - suvbwToSuvlbm?: number; - suvbwToSuvbsa?: number; - }; - }; - // (undocumented) - sharedCacheKey?: string; - sizeInBytes: number; - sliceThickness?: number; - slope: number; - stats?: { - lastStoredPixelDataToCanvasImageDataTime?: number; - lastGetPixelDataTime?: number; - lastPutImageDataTime?: number; - lastLutGenerateTime?: number; - lastRenderedViewport?: unknown; - lastRenderTime?: number; - }; - voiLUT?: CPUFallbackLUT; - voiLUTFunction: string; - width: number; - windowCenter: number[] | number; - windowWidth: number[] | number; -} - -// @public -interface IImageCalibration { - aspect?: number; - // (undocumented) - columnPixelSpacing?: number; - rowPixelSpacing?: number; - scale?: number; - sequenceOfUltrasoundRegions?: Record[]; - tooltip?: string; - type: CalibrationTypes; -} - -// @public -interface IImageData { - // (undocumented) - calibration?: IImageCalibration; - dimensions: Point3; - direction: Mat3; - hasPixelSpacing?: boolean; - imageData: vtkImageData; - metadata: { Modality: string }; - origin: Point3; - preScale?: { - scaled?: boolean; - scalingParameters?: { - modality?: string; - rescaleSlope?: number; - rescaleIntercept?: number; - suvbw?: number; - }; - }; - scalarData: Float32Array | Uint16Array | Uint8Array | Int16Array; - scaling?: Scaling; - spacing: Point3; -} - -// @public -interface IImageLoadObject { - cancelFn?: () => void; - decache?: () => void; - promise: Promise; -} - -// @public -interface IImageVolume { - // (undocumented) - cancelLoading?: () => void; - convertToCornerstoneImage?: ( - imageId: string, - imageIdIndex: number - ) => IImageLoadObject; - destroy(): void; - dimensions: Point3; - direction: Mat3; - getImageIdIndex(imageId: string): number; - getImageURIIndex(imageURI: string): number; - getScalarData(): VolumeScalarData; - hasPixelSpacing: boolean; - imageData?: vtkImageData; - imageIds: Array; - isDynamicVolume(): boolean; - isPreScaled: boolean; - loadStatus?: Record; - metadata: Metadata; - numVoxels: number; - origin: Point3; - referencedVolumeId?: string; - scaling?: { - PT?: { - SUVlbmFactor?: number; - SUVbsaFactor?: number; - suvbwToSuvlbm?: number; - suvbwToSuvbsa?: number; - }; - }; - sizeInBytes?: number; - spacing: Point3; - readonly volumeId: string; - vtkOpenGLTexture: any; -} - -// @public -type ImageCacheImageAddedEvent = -CustomEvent_2; - -// @public -type ImageCacheImageAddedEventDetail = { - image: ICachedImage; -}; - -// @public -type ImageCacheImageRemovedEvent = -CustomEvent_2; - -// @public -type ImageCacheImageRemovedEventDetail = { - imageId: string; -}; - -// @public -type ImageLoadedEvent = CustomEvent_2; - -// @public -type ImageLoadedEventDetail = { - image: IImage; -}; - -// @public -type ImageLoadedFailedEvent = CustomEvent_2; - -// @public -type ImageLoadedFailedEventDetail = { - imageId: string; - error: unknown; -}; - -// @public -type ImageLoaderFn = ( -imageId: string, -options?: Record -) => { - promise: Promise>; - cancelFn?: () => void | undefined; - decache?: () => void | undefined; -}; - -// @public -type ImageLoadProgressEvent = CustomEvent_2; - -// @public -type ImageLoadProgressEventDetail = { - url: string; - imageId: string; - loaded: number; - total: number; - percent: number; -}; - -// @public (undocumented) -interface ImagePixelModule { - // (undocumented) - bitsAllocated: number; - // (undocumented) - bitsStored: number; - // (undocumented) - highBit: number; - // (undocumented) - modality: string; - // (undocumented) - photometricInterpretation: string; - // (undocumented) - pixelRepresentation: string; - // (undocumented) - samplesPerPixel: number; - // (undocumented) - voiLUTFunction: VOILUTFunctionType; - // (undocumented) - windowCenter: number | number[]; - // (undocumented) - windowWidth: number | number[]; -} - -// @public (undocumented) -interface ImagePlaneModule { - // (undocumented) - columnCosines?: Point3; - // (undocumented) - columnPixelSpacing?: number; - // (undocumented) - columns: number; - // (undocumented) - frameOfReferenceUID: string; - // (undocumented) - imageOrientationPatient?: Float32Array; - // (undocumented) - imagePositionPatient?: Point3; - // (undocumented) - pixelSpacing?: Point2; - // (undocumented) - rowCosines?: Point3; - // (undocumented) - rowPixelSpacing?: number; - // (undocumented) - rows: number; - // (undocumented) - sliceLocation?: number; - // (undocumented) - sliceThickness?: number; -} - -// @public -type ImageRenderedEvent = CustomEvent_2; - -// @public -type ImageRenderedEventDetail = { - element: HTMLDivElement; - viewportId: string; - renderingEngineId: string; - suppressEvents?: boolean; - viewportStatus: ViewportStatus; -}; - -// @public (undocumented) -type ImageSliceData = { - numberOfSlices: number; - imageIndex: number; -}; - -// @public -type ImageSpacingCalibratedEvent = -CustomEvent_2; - -// @public -type ImageSpacingCalibratedEventDetail = { - element: HTMLDivElement; - viewportId: string; - renderingEngineId: string; - imageId: string; - calibration: IImageCalibration; - imageData: vtkImageData; - worldToIndex: mat4; -}; - -// @public -type ImageVolumeLoadingCompletedEvent = -CustomEvent_2; - -// @public -type ImageVolumeLoadingCompletedEventDetail = { - volumeId: string; - FrameOfReferenceUID: string; -}; - -// @public -type ImageVolumeModifiedEvent = CustomEvent_2; - -// @public -type ImageVolumeModifiedEventDetail = { - imageVolume: IImageVolume; - FrameOfReferenceUID: string; -}; - -// @public -interface IRegisterImageLoader { - // (undocumented) - registerImageLoader: (scheme: string, imageLoader: ImageLoaderFn) => void; -} - -// @public (undocumented) -interface IRenderingEngine { - // (undocumented) - _debugRender(): void; - // (undocumented) - destroy(): void; - // (undocumented) - disableElement(viewportId: string): void; - // (undocumented) - enableElement(viewportInputEntry: PublicViewportInput): void; - // (undocumented) - fillCanvasWithBackgroundColor( - canvas: HTMLCanvasElement, - backgroundColor: [number, number, number] - ): void; - // (undocumented) - getStackViewports(): Array; - // (undocumented) - getViewport(id: string): IStackViewport | IVolumeViewport; - // (undocumented) - getViewports(): Array; - // (undocumented) - getVolumeViewports(): Array; - // (undocumented) - hasBeenDestroyed: boolean; - // (undocumented) - id: string; - // (undocumented) - offScreenCanvasContainer: any; - // (undocumented) - offscreenMultiRenderWindow: any; - // (undocumented) - render(): void; - // (undocumented) - renderFrameOfReference(FrameOfReferenceUID: string): void; - // (undocumented) - renderViewport(viewportId: string): void; - // (undocumented) - renderViewports(viewportIds: Array): void; - // (undocumented) - resize(immediate?: boolean, keepCamera?: boolean): void; - // (undocumented) - setViewports(viewports: Array): void; -} - -// @public -interface IStackViewport extends IViewport { - calibrateSpacing(imageId: string): void; - canvasToWorld: (canvasPos: Point2) => Point3; - clearDefaultProperties(imageId?: string): void; - customRenderViewportToCanvas: () => { - canvas: HTMLCanvasElement; - element: HTMLDivElement; - viewportId: string; - renderingEngineId: string; - }; - getCamera(): ICamera; - getCornerstoneImage: () => IImage; - getCurrentImageId: () => string; - getCurrentImageIdIndex: () => number; - getDefaultProperties: (imageId?: string) => StackViewportProperties; - getFrameOfReferenceUID: () => string; - getImageData(): IImageData | CPUIImageData; - getImageIds: () => string[]; - getProperties: () => StackViewportProperties; - getRenderer(): any; - hasImageId: (imageId: string) => boolean; - hasImageURI: (imageURI: string) => boolean; - // (undocumented) - modality: string; - resetCamera(resetPan?: boolean, resetZoom?: boolean): boolean; - resetProperties(): void; - resetToDefaultProperties(): void; - resize: () => void; - scaling: Scaling; - setCamera(cameraInterface: ICamera): void; - setDefaultProperties( - ViewportProperties: StackViewportProperties, - imageId?: string - ): void; - setImageIdIndex(imageIdIndex: number): Promise; - setProperties( - { - voiRange, - invert, - interpolationType, - rotation, - colormap, - }: StackViewportProperties, - suppressEvents?: boolean - ): void; - setStack( - imageIds: Array, - currentImageIdIndex?: number - ): Promise; - unsetColormap(): void; - worldToCanvas: (worldPos: Point3) => Point2; -} - -// @public -interface IStreamingImageVolume extends ImageVolume { - clearLoadCallbacks(): void; - convertToCornerstoneImage(imageId: string, imageIdIndex: number): any; - decache(completelyRemove: boolean): void; -} - -// @public (undocumented) -interface IStreamingVolumeProperties { - imageIds: Array; - - loadStatus: { - loaded: boolean; - loading: boolean; - cancelled: boolean; - cachedFrames: Array; - callbacks: Array<() => void>; - }; -} - -// @public -interface IViewport { - _actors: Map; - addActor(actorEntry: ActorEntry): void; - addActors(actors: Array): void; - canvas: HTMLCanvasElement; - canvasToWorld: (canvasPos: Point2) => Point3; - customRenderViewportToCanvas: () => unknown; - defaultOptions: any; - element: HTMLDivElement; - getActor(actorUID: string): ActorEntry; - getActorByIndex(index: number): ActorEntry; - getActors(): Array; - getActorUIDByIndex(index: number): string; - getCamera(): ICamera; - getCanvas(): HTMLCanvasElement; - // (undocumented) - _getCorners(bounds: Array): Array[]; - getDefaultActor(): ActorEntry; - getDisplayArea(): DisplayArea | undefined; - getFrameOfReferenceUID: () => string; - getPan(): Point2; - getRenderer(): void; - getRenderingEngine(): any; - getRotation: () => number; - getZoom(): number; - id: string; - isDisabled: boolean; - options: ViewportInputOptions; - removeActors(actorUIDs: Array): void; - removeAllActors(): void; - render(): void; - renderingEngineId: string; - reset(immediate: boolean): void; - setActors(actors: Array): void; - setCamera(cameraInterface: ICamera, storeAsInitialCamera?: boolean): void; - setDisplayArea( - displayArea: DisplayArea, - callResetCamera?: boolean, - suppressEvents?: boolean - ); - setOptions(options: ViewportInputOptions, immediate: boolean): void; - setPan(pan: Point2, storeAsInitialCamera?: boolean); - setRendered(): void; - setZoom(zoom: number, storeAsInitialCamera?: boolean); - sHeight: number; - suppressEvents: boolean; - sWidth: number; - sx: number; - sy: number; - type: ViewportType; - // (undocumented) - updateRenderingPipeline: () => void; - viewportStatus: ViewportStatus; - worldToCanvas: (worldPos: Point3) => Point2; -} - -// @public -interface IViewportId { - // (undocumented) - renderingEngineId: string; - // (undocumented) - viewportId: string; -} - -// @public -interface IVolume { - dimensions: Point3; - direction: Mat3; - imageData?: vtkImageData; - metadata: Metadata; - origin: Point3; - referencedVolumeId?: string; - scalarData: VolumeScalarData | Array; - scaling?: { - PT?: { - // @TODO: Do these values exist? - SUVlbmFactor?: number; - SUVbsaFactor?: number; - // accessed in ProbeTool - suvbwToSuvlbm?: number; - suvbwToSuvbsa?: number; - }; - }; - sizeInBytes?: number; - spacing: Point3; - volumeId: string; -} - -// @public -interface IVolumeInput { - // (undocumented) - actorUID?: string; - // actorUID for segmentations, since two segmentations with the same volumeId - // can have different representations - blendMode?: BlendModes; - // actorUID for segmentations, since two segmentations with the same volumeId - // can have different representations - callback?: VolumeInputCallback; - // actorUID for segmentations, since two segmentations with the same volumeId - // can have different representations - slabThickness?: number; - // actorUID for segmentations, since two segmentations with the same volumeId - // can have different representations - visibility?: boolean; - // actorUID for segmentations, since two segmentations with the same volumeId - // can have different representations - volumeId: string; -} - -// @public -interface IVolumeLoadObject { - cancelFn?: () => void; - decache?: () => void; - promise: Promise; -} - -// @public -interface IVolumeViewport extends IViewport { - addVolumes( - volumeInputArray: Array, - immediate?: boolean, - suppressEvents?: boolean - ): Promise; - canvasToWorld: (canvasPos: Point2) => Point3; - clearDefaultProperties(volumeId?: string): void; - flip(flipDirection: FlipDirection): void; - getBounds(): any; - getCurrentImageId: () => string; - getCurrentImageIdIndex: () => number; - getDefaultProperties: (volumeId?: string) => VolumeViewportProperties; - // (undocumented) - getFrameOfReferenceUID: () => string; - getImageData(volumeId?: string): IImageData | undefined; - getImageIds: (volumeId?: string) => string[]; - getIntensityFromWorld(point: Point3): number; - getProperties: (volumeId?: string) => VolumeViewportProperties; - getSlabThickness(): number; - hasImageURI: (imageURI: string) => boolean; - hasVolumeId: (volumeId: string) => boolean; - removeVolumeActors(actorUIDs: Array, immediate?: boolean): void; - resetCamera( - resetPan?: boolean, - resetZoom?: boolean, - resetToCenter?: boolean - ): boolean; - resetProperties(volumeId: string): void; - setBlendMode( - blendMode: BlendModes, - filterActorUIDs?: Array, - immediate?: boolean - ): void; - setDefaultProperties( - ViewportProperties: VolumeViewportProperties, - volumeId?: string - ): void; - // (undocumented) - setOrientation(orientation: OrientationAxis): void; - setProperties( - { voiRange }: VolumeViewportProperties, - volumeId?: string, - suppressEvents?: boolean - ): void; - setSlabThickness( - slabThickness: number, - filterActorUIDs?: Array - ): void; - setVolumes( - volumeInputArray: Array, - immediate?: boolean, - suppressEvents?: boolean - ): Promise; - // (undocumented) - useCPURendering: boolean; - worldToCanvas: (worldPos: Point3) => Point2; -} - -// @public (undocumented) -function makeVolumeMetadata(niftiHeader: any, orientation: any, scalarData: any): Types.Metadata; - -// @public -type Mat3 = -| [number, number, number, number, number, number, number, number, number] -| Float32Array; - -// @public -type Metadata = { - BitsAllocated: number; - BitsStored: number; - SamplesPerPixel: number; - HighBit: number; - PhotometricInterpretation: string; - PixelRepresentation: number; - Modality: string; - SeriesInstanceUID?: string; - ImageOrientationPatient: Array; - PixelSpacing: Array; - FrameOfReferenceUID: string; - Columns: number; - Rows: number; - voiLut: Array; - VOILUTFunction: string; -}; - -// @public (undocumented) -function modalityScaleNifti(array: Float32Array | Int16Array | Uint8Array, niftiHeader: any): void; - -// @public (undocumented) -export class NiftiImageVolume extends ImageVolume { - constructor(imageVolumeProperties: Types.IVolume, streamingProperties: NiftiImageProperties); - // (undocumented) - cancelLoading: () => void; - // (undocumented) - clearLoadCallbacks(): void; - // (undocumented) - controller: AbortController; - // (undocumented) - decache(): void; - // (undocumented) - load: (callback: (...args: unknown[]) => void, priority?: number) => void; - // (undocumented) - loadStatus: LoadStatus; -} - -// @public -type OrientationVectors = { - viewPlaneNormal: Point3; - viewUp: Point3; -}; - -// @public (undocumented) -type PixelDataTypedArray = -| Float32Array -| Int16Array -| Uint16Array -| Uint8Array -| Int8Array -| Uint8ClampedArray; - -// @public -type Plane = [number, number, number, number]; - -// @public -type Point2 = [number, number]; - -// @public -type Point3 = [number, number, number]; - -// @public -type Point4 = [number, number, number, number]; - -// @public -type PreStackNewImageEvent = CustomEvent_2; - -// @public -type PreStackNewImageEventDetail = { - imageId: string; - imageIdIndex: number; - viewportId: string; - renderingEngineId: string; -}; - -// @public (undocumented) -type PTScaling = { - suvbwToSuvlbm?: number; - suvbwToSuvbsa?: number; - suvbw?: number; - suvlbm?: number; - suvbsa?: number; -}; - -// @public (undocumented) -type PublicContourSetData = ContourSetData; - -// @public (undocumented) -type PublicSurfaceData = { - id: string; - data: SurfaceData; - frameOfReferenceUID: string; - color?: Point3; -}; - -// @public -type PublicViewportInput = { - element: HTMLDivElement; - viewportId: string; - type: ViewportType; - defaultOptions?: ViewportInputOptions; -}; - -// @public -type RGB = [number, number, number]; - -// @public (undocumented) -type Scaling = { - PT?: PTScaling; -}; - -// @public (undocumented) -type ScalingParameters = { - rescaleSlope: number; - rescaleIntercept: number; - modality: string; - suvbw?: number; - suvlbm?: number; - suvbsa?: number; -}; - -// @public -type StackNewImageEvent = CustomEvent_2; - -// @public -type StackNewImageEventDetail = { - image: IImage; - imageId: string; - imageIdIndex: number; - viewportId: string; - renderingEngineId: string; -}; - -// @public -type StackViewportNewStackEvent = -CustomEvent_2; - -// @public -type StackViewportNewStackEventDetail = { - imageIds: string[]; - viewportId: string; - element: HTMLDivElement; - currentImageIdIndex: number; -}; - -// @public -type StackViewportProperties = ViewportProperties & { - interpolationType?: InterpolationType; - rotation?: number; - suppressEvents?: boolean; - isComputedVOI?: boolean; -}; - -// @public (undocumented) -type StackViewportScrollEvent = CustomEvent_2; - -// @public -type StackViewportScrollEventDetail = { - newImageIdIndex: number; - imageId: string; - direction: number; -}; - -// @public (undocumented) -type SurfaceData = { - points: number[]; - polys: number[]; -}; - -// @public -type TransformMatrix2D = [number, number, number, number, number, number]; - -// @public -type ViewportInputOptions = { - background?: RGB; - orientation?: OrientationAxis | OrientationVectors; - displayArea?: DisplayArea; - suppressEvents?: boolean; - parallelProjection?: boolean; -}; - -// @public (undocumented) -interface ViewportPreset { - // (undocumented) - ambient: string; - // (undocumented) - colorTransfer: string; - // (undocumented) - diffuse: string; - // (undocumented) - gradientOpacity: string; - // (undocumented) - interpolation: string; - // (undocumented) - name: string; - // (undocumented) - scalarOpacity: string; - // (undocumented) - shade: string; - // (undocumented) - specular: string; - // (undocumented) - specularPower: string; -} - -// @public -type ViewportProperties = { - voiRange?: VOIRange; - VOILUTFunction?: VOILUTFunctionType; - invert?: boolean; - colormap?: ColormapPublic; - interpolationType?: InterpolationType; -}; - -// @public (undocumented) -type VOI = { - windowWidth: number; - windowCenter: number; -}; - -// @public -type VoiModifiedEvent = CustomEvent_2; - -// @public -type VoiModifiedEventDetail = { - viewportId: string; - range: VOIRange; - volumeId?: string; - VOILUTFunction?: VOILUTFunctionType; - invert?: boolean; - invertStateChanged?: boolean; -}; - -// @public (undocumented) -type VOIRange = { - upper: number; - lower: number; -}; - -// @public (undocumented) -type VolumeActor = vtkVolume; - -// @public -type VolumeCacheVolumeAddedEvent = -CustomEvent_2; - -// @public -type VolumeCacheVolumeAddedEventDetail = { - volume: ICachedVolume; -}; - -// @public -type VolumeCacheVolumeRemovedEvent = -CustomEvent_2; - -// @public -type VolumeCacheVolumeRemovedEventDetail = { - volumeId: string; -}; - -// @public -type VolumeInputCallback = (params: { - volumeActor: VolumeActor; - volumeId: string; -}) => unknown; - -// @public -type VolumeLoadedEvent = CustomEvent_2; - -// @public -type VolumeLoadedEventDetail = { - volume: IImageVolume; -}; - -// @public -type VolumeLoadedFailedEvent = CustomEvent_2; - -// @public -type VolumeLoadedFailedEventDetail = { - volumeId: string; - error: unknown; -}; - -// @public -type VolumeLoaderFn = ( -volumeId: string, -options?: Record -) => { - promise: Promise>; - cancelFn?: () => void | undefined; - decache?: () => void | undefined; -}; - -// @public -type VolumeNewImageEvent = CustomEvent_2; - -// @public -type VolumeNewImageEventDetail = { - imageIndex: number; - numberOfSlices: number; - viewportId: string; - renderingEngineId: string; -}; - -// @public (undocumented) -type VolumeScalarData = Float32Array | Uint8Array | Uint16Array | Int16Array; - -// @public -type VolumeViewportProperties = ViewportProperties & { - preset?: string; - - slabThickness?: number; -}; - -// (No @packageDocumentation comment for this package) - -``` +## API Report File for "@cornerstonejs/nifti-volume-loader" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts + +import type { GetGPUTier } from 'detect-gpu'; +import type { mat4 } from 'gl-matrix'; +import type { TierResult } from 'detect-gpu'; +import type vtkActor from '@kitware/vtk.js/Rendering/Core/Actor'; +import type { vtkImageData } from '@kitware/vtk.js/Common/DataModel/ImageData'; +import vtkImageSlice from '@kitware/vtk.js/Rendering/Core/ImageSlice'; +import type vtkVolume from '@kitware/vtk.js/Rendering/Core/Volume'; + +// @public (undocumented) +type Actor = vtkActor; + +// @public +type ActorEntry = { + uid: string; + actor: Actor | VolumeActor | ImageActor; + referenceId?: string; + slabThickness?: number; + clippingFilter?: any; +}; + +// @public +type ActorSliceRange = { + actor: VolumeActor; + viewPlaneNormal: Point3; + focalPoint: Point3; + min: number; + max: number; + current: number; +}; + +// @public (undocumented) +type AffineMatrix = [ +[number, number, number, number], +[number, number, number, number], +[number, number, number, number], +[number, number, number, number] +]; + +// @public +type CameraModifiedEvent = CustomEvent_2; + +// @public +type CameraModifiedEventDetail = { + previousCamera: ICamera; + camera: ICamera; + element: HTMLDivElement; + viewportId: string; + renderingEngineId: string; + rotation?: number; +}; + +// @public (undocumented) +type ColormapPublic = { + name?: string; + opacity?: OpacityMapping[] | number; + /** midpoint mapping between values to opacity if the colormap + * is getting used for fusion, this is an array of arrays which + * each array containing 2 values, the first value is the value + * to map to opacity and the second value is the opacity value. + * By default, the minimum value is mapped to 0 opacity and the + * maximum value is mapped to 1 opacity, but you can configure + * the points in the middle to be mapped to different opacities + * instead of a linear mapping from 0 to 1. + */ +}; + +// @public (undocumented) +type ColormapRegistration = { + ColorSpace: string; + Name: string; + RGBPoints: RGB[]; +}; + +// @public (undocumented) +type ContourData = { + points: Point3[]; + type: ContourType; + color: Point3; + segmentIndex: number; +}; + +// @public (undocumented) +type ContourSetData = { + id: string; + data: ContourData[]; + frameOfReferenceUID: string; + color?: Point3; + segmentIndex?: number; +}; + +// @public (undocumented) +type Cornerstone3DConfig = { + gpuTier?: TierResult; + detectGPUConfig: GetGPUTier; + rendering: { + // vtk.js supports 8bit integer textures and 32bit float textures. + // However, if the client has norm16 textures (it can be seen by visiting + // the webGl report at https://webglreport.com/?v=2), vtk will be default + // to use it to improve memory usage. However, if the client don't have + // it still another level of optimization can happen by setting the + // preferSizeOverAccuracy since it will reduce the size of the texture to half + // float at the cost of accuracy in rendering. This is a tradeoff that the + // client can decide. + // + // Read more in the following Pull Request: + // 1. HalfFloat: https://github.com/Kitware/vtk-js/pull/2046 + // 2. Norm16: https://github.com/Kitware/vtk-js/pull/2058 + preferSizeOverAccuracy: boolean; + // Whether the EXT_texture_norm16 extension is supported by the browser. + // WebGL 2 report (link: https://webglreport.com/?v=2) can be used to check + // if the browser supports this extension. + // In case the browser supports this extension, instead of using 32bit float + // textures, 16bit float textures will be used to reduce the memory usage where + // possible. + // Norm16 may not work currently due to the two active bugs in chrome + safari + // https://bugs.chromium.org/p/chromium/issues/detail?id=1408247 + // https://bugs.webkit.org/show_bug.cgi?id=252039 + useNorm16Texture: boolean; + useCPURendering: boolean; + strictZSpacingForVolumeViewport: boolean; + }; +}; + +// @public (undocumented) +export function cornerstoneNiftiImageVolumeLoader(volumeId: string): IVolumeLoader; + +// @public (undocumented) +interface CPUFallbackColormap { + // (undocumented) + addColor: (rgba: Point4) => void; + // (undocumented) + buildLookupTable: (lut: CPUFallbackLookupTable) => void; + // (undocumented) + clearColors: () => void; + // (undocumented) + createLookupTable: () => CPUFallbackLookupTable; + // (undocumented) + getColor: (index: number) => Point4; + // (undocumented) + getColorRepeating: (index: number) => Point4; + // (undocumented) + getColorSchemeName: () => string; + getId: () => string; + // (undocumented) + getNumberOfColors: () => number; + // (undocumented) + insertColor: (index: number, rgba: Point4) => void; + // (undocumented) + isValidIndex: (index: number) => boolean; + // (undocumented) + removeColor: (index: number) => void; + // (undocumented) + setColor: (index: number, rgba: Point4) => void; + // (undocumented) + setColorSchemeName: (name: string) => void; + // (undocumented) + setNumberOfColors: (numColors: number) => void; +} + +// @public (undocumented) +type CPUFallbackColormapData = { + name: string; + numOfColors?: number; + colors?: Point4[]; + segmentedData?: unknown; + numColors?: number; + gamma?: number; +}; + +// @public (undocumented) +type CPUFallbackColormapsData = { + [key: string]: CPUFallbackColormapData; +}; + +// @public (undocumented) +interface CPUFallbackEnabledElement { + // (undocumented) + canvas?: HTMLCanvasElement; + // (undocumented) + colormap?: CPUFallbackColormap; + // (undocumented) + image?: IImage; + // (undocumented) + invalid?: boolean; + // (undocumented) + metadata?: { + direction?: Mat3; + dimensions?: Point3; + spacing?: Point3; + origin?: Point3; + imagePlaneModule?: ImagePlaneModule; + imagePixelModule?: ImagePixelModule; + }; + // (undocumented) + needsRedraw?: boolean; + // (undocumented) + options?: { + [key: string]: unknown; + colormap?: CPUFallbackColormap; + }; + // (undocumented) + pan?: Point2; + // (undocumented) + renderingTools?: CPUFallbackRenderingTools; + // (undocumented) + rotation?: number; + // (undocumented) + scale?: number; + // (undocumented) + transform?: CPUFallbackTransform; + // (undocumented) + viewport?: CPUFallbackViewport; + // (undocumented) + zoom?: number; +} + +// @public (undocumented) +interface CPUFallbackLookupTable { + // (undocumented) + build: (force: boolean) => void; + // (undocumented) + getColor: (scalar: number) => Point4; + // (undocumented) + setAlphaRange: (start: number, end: number) => void; + // (undocumented) + setHueRange: (start: number, end: number) => void; + // (undocumented) + setNumberOfTableValues: (number: number) => void; + // (undocumented) + setRamp: (ramp: string) => void; + // (undocumented) + setRange: (start: number, end: number) => void; + // (undocumented) + setSaturationRange: (start: number, end: number) => void; + // (undocumented) + setTableRange: (start: number, end: number) => void; + // (undocumented) + setTableValue(index: number, rgba: Point4); + // (undocumented) + setValueRange: (start: number, end: number) => void; +} + +// @public (undocumented) +type CPUFallbackLUT = { + lut: number[]; +}; + +// @public (undocumented) +type CPUFallbackRenderingTools = { + renderCanvas?: HTMLCanvasElement; + lastRenderedIsColor?: boolean; + lastRenderedImageId?: string; + lastRenderedViewport?: { + windowWidth: number | number[]; + windowCenter: number | number[]; + invert: boolean; + rotation: number; + hflip: boolean; + vflip: boolean; + modalityLUT: CPUFallbackLUT; + voiLUT: CPUFallbackLUT; + colormap: unknown; + }; + renderCanvasContext?: CanvasRenderingContext2D; + colormapId?: string; + colorLUT?: CPUFallbackLookupTable; + renderCanvasData?: ImageData; +}; + +// @public (undocumented) +interface CPUFallbackTransform { + // (undocumented) + clone: () => CPUFallbackTransform; + // (undocumented) + getMatrix: () => TransformMatrix2D; + // (undocumented) + invert: () => void; + // (undocumented) + multiply: (matrix: TransformMatrix2D) => void; + // (undocumented) + reset: () => void; + // (undocumented) + rotate: (rad: number) => void; + // (undocumented) + scale: (sx: number, sy: number) => void; + // (undocumented) + transformPoint: (point: Point2) => Point2; + // (undocumented) + translate: (x: number, y: number) => void; +} + +// @public (undocumented) +type CPUFallbackViewport = { + scale?: number; + parallelScale?: number; + focalPoint?: number[]; + translation?: { + x: number; + y: number; + }; + voi?: { + windowWidth: number; + windowCenter: number; + }; + invert?: boolean; + pixelReplication?: boolean; + rotation?: number; + hflip?: boolean; + vflip?: boolean; + modalityLUT?: CPUFallbackLUT; + voiLUT?: CPUFallbackLUT; + colormap?: CPUFallbackColormap; + displayedArea?: CPUFallbackViewportDisplayedArea; + modality?: string; +}; + +// @public (undocumented) +type CPUFallbackViewportDisplayedArea = { + tlhc: { + x: number; + y: number; + }; + brhc: { + x: number; + y: number; + }; + rowPixelSpacing: number; + columnPixelSpacing: number; + presentationSizeMode: string; +}; + +// @public (undocumented) +type CPUIImageData = { + dimensions: Point3; + direction: Mat3; + spacing: Point3; + origin: Point3; + imageData: CPUImageData; + metadata: { Modality: string }; + scalarData: PixelDataTypedArray; + scaling: Scaling; + hasPixelSpacing?: boolean; + calibration?: IImageCalibration; + + preScale?: { + scaled?: boolean; + scalingParameters?: { + modality?: string; + rescaleSlope?: number; + rescaleIntercept?: number; + suvbw?: number; + }; + }; +}; + +// @public (undocumented) +type CPUImageData = { + worldToIndex?: (point: Point3) => Point3; + indexToWorld?: (point: Point3) => Point3; + getWorldToIndex?: () => Point3; + getIndexToWorld?: () => Point3; + getSpacing?: () => Point3; + getDirection?: () => Mat3; + getScalarData?: () => PixelDataTypedArray; + getDimensions?: () => Point3; +}; + +// @public (undocumented) +interface CustomEvent_2 extends Event { + readonly detail: T; + // (undocumented) + initCustomEvent( + typeArg: string, + canBubbleArg: boolean, + cancelableArg: boolean, + detailArg: T + ): void; +} + +// @public (undocumented) +type DisplayArea = { + imageArea: [number, number]; // areaX, areaY + imageCanvasPoint: { + imagePoint: [number, number]; // imageX, imageY + canvasPoint: [number, number]; // canvasX, canvasY + }; + storeAsInitialCamera: boolean; +}; + +// @public +type DisplayAreaModifiedEvent = CustomEvent_2; + +// @public +type DisplayAreaModifiedEventDetail = { + viewportId: string; + displayArea: DisplayArea; + volumeId?: string; + storeAsInitialCamera?: boolean; +}; + +// @public +type ElementDisabledEvent = CustomEvent_2; + +// @public +type ElementDisabledEventDetail = { + element: HTMLDivElement; + viewportId: string; + renderingEngineId: string; +}; + +// @public +type ElementEnabledEvent = CustomEvent_2; + +// @public +type ElementEnabledEventDetail = { + element: HTMLDivElement; + viewportId: string; + renderingEngineId: string; +}; + +declare namespace Enums { + export { + Events + } +} +export { Enums } + +// @public (undocumented) +enum Events { + // (undocumented) + NIFTI_VOLUME_LOADED = "CORNERSTONE_NIFTI_VOLUME_LOADED", + // (undocumented) + NIFTI_VOLUME_PROGRESS = "CORNERSTONE_NIFTI_VOLUME_PROGRESS" +} + +declare namespace EventTypes { + export { + CameraModifiedEventDetail, + CameraModifiedEvent, + VoiModifiedEvent, + VoiModifiedEventDetail, + DisplayAreaModifiedEvent, + DisplayAreaModifiedEventDetail, + ElementDisabledEvent, + ElementDisabledEventDetail, + ElementEnabledEvent, + ElementEnabledEventDetail, + ImageRenderedEventDetail, + ImageRenderedEvent, + ImageVolumeModifiedEvent, + ImageVolumeModifiedEventDetail, + ImageVolumeLoadingCompletedEvent, + ImageVolumeLoadingCompletedEventDetail, + ImageLoadedEvent, + ImageLoadedEventDetail, + ImageLoadedFailedEventDetail, + ImageLoadedFailedEvent, + VolumeLoadedEvent, + VolumeLoadedEventDetail, + VolumeLoadedFailedEvent, + VolumeLoadedFailedEventDetail, + ImageCacheImageAddedEvent, + ImageCacheImageAddedEventDetail, + ImageCacheImageRemovedEvent, + ImageCacheImageRemovedEventDetail, + VolumeCacheVolumeAddedEvent, + VolumeCacheVolumeAddedEventDetail, + VolumeCacheVolumeRemovedEvent, + VolumeCacheVolumeRemovedEventDetail, + StackNewImageEvent, + StackNewImageEventDetail, + PreStackNewImageEvent, + PreStackNewImageEventDetail, + ImageSpacingCalibratedEvent, + ImageSpacingCalibratedEventDetail, + ImageLoadProgressEvent, + ImageLoadProgressEventDetail, + VolumeNewImageEvent, + VolumeNewImageEventDetail, + StackViewportNewStackEvent, + StackViewportNewStackEventDetail, + StackViewportScrollEvent, + StackViewportScrollEventDetail + } +} + +// @public (undocumented) +function fetchAndAllocateNiftiVolume(volumeId: string): Promise; + +// @public +type FlipDirection = { + flipHorizontal?: boolean; + flipVertical?: boolean; +}; + +declare namespace helpers { + export { + modalityScaleNifti, + makeVolumeMetadata, + fetchAndAllocateNiftiVolume + } +} +export { helpers } + +// @public (undocumented) +interface ICache { + getCacheSize: () => number; + getImageLoadObject: (imageId: string) => IImageLoadObject | void; + getMaxCacheSize: () => number; + getVolumeLoadObject: (volumeId: string) => IVolumeLoadObject | void; + purgeCache: () => void; + putImageLoadObject: ( + imageId: string, + imageLoadObject: IImageLoadObject + ) => Promise; + putVolumeLoadObject: ( + volumeId: string, + volumeLoadObject: IVolumeLoadObject + ) => Promise; + setMaxCacheSize: (maxCacheSize: number) => void; +} + +// @public (undocumented) +interface ICachedGeometry { + // (undocumented) + geometry?: IGeometry; + // (undocumented) + geometryId: string; + // (undocumented) + geometryLoadObject: IGeometryLoadObject; + // (undocumented) + loaded: boolean; + // (undocumented) + sizeInBytes: number; + // (undocumented) + timeStamp: number; +} + +// @public (undocumented) +interface ICachedImage { + // (undocumented) + image?: IImage; + // (undocumented) + imageId: string; + // (undocumented) + imageLoadObject: IImageLoadObject; + // (undocumented) + loaded: boolean; + // (undocumented) + sharedCacheKey?: string; + // (undocumented) + sizeInBytes: number; + // (undocumented) + timeStamp: number; +} + +// @public (undocumented) +interface ICachedVolume { + // (undocumented) + loaded: boolean; + // (undocumented) + sizeInBytes: number; + // (undocumented) + timeStamp: number; + // (undocumented) + volume?: IImageVolume; + // (undocumented) + volumeId: string; + // (undocumented) + volumeLoadObject: IVolumeLoadObject; +} + +// @public +interface ICamera { + clippingRange?: Point2; + flipHorizontal?: boolean; + flipVertical?: boolean; + focalPoint?: Point3; + parallelProjection?: boolean; + parallelScale?: number; + position?: Point3; + scale?: number; + viewAngle?: number; + viewPlaneNormal?: Point3; + viewUp?: Point3; +} + +// @public (undocumented) +interface IContour { + // (undocumented) + color: any; + // (undocumented) + getColor(): Point3; + // (undocumented) + getFlatPointsArray(): number[]; + getPoints(): Point3[]; + // (undocumented) + _getSizeInBytes(): number; + // (undocumented) + getType(): ContourType; + // (undocumented) + readonly id: string; + // (undocumented) + points: Point3[]; + // (undocumented) + readonly sizeInBytes: number; +} + +// @public +interface IContourSet { + // (undocumented) + contours: IContour[]; + // (undocumented) + _createEachContour(data: ContourData[]): void; + // (undocumented) + readonly frameOfReferenceUID: string; + // (undocumented) + getCentroid(): Point3; + // (undocumented) + getColor(): any; + getContours(): IContour[]; + getFlatPointsArray(): Point3[]; + getNumberOfContours(): number; + getNumberOfPointsArray(): number[]; + getNumberOfPointsInAContour(contourIndex: number): number; + getPointsInContour(contourIndex: number): Point3[]; + // (undocumented) + getSegmentIndex(): number; + // (undocumented) + getSizeInBytes(): number; + getTotalNumberOfPoints(): number; + // (undocumented) + readonly id: string; + // (undocumented) + readonly sizeInBytes: number; +} + +// @public +interface IDynamicImageVolume extends IImageVolume { + getScalarDataArrays(): VolumeScalarData[]; + get numTimePoints(): number; + get timePointIndex(): number; + set timePointIndex(newTimePointIndex: number); +} + +// @public +interface IEnabledElement { + FrameOfReferenceUID: string; + renderingEngine: IRenderingEngine; + renderingEngineId: string; + viewport: IStackViewport | IVolumeViewport; + viewportId: string; +} + +// @public (undocumented) +interface IGeometry { + // (undocumented) + data: IContourSet | Surface; + // (undocumented) + id: string; + // (undocumented) + sizeInBytes: number; + // (undocumented) + type: GeometryType; +} + +// @public (undocumented) +interface IGeometryLoadObject { + cancelFn?: () => void; + decache?: () => void; + promise: Promise; +} + +// @public +interface IImage { + cachedLut?: { + windowWidth?: number | number[]; + windowCenter?: number | number[]; + invert?: boolean; + lutArray?: Uint8ClampedArray; + modalityLUT?: unknown; + voiLUT?: CPUFallbackLUT; + }; + color: boolean; + colormap?: CPUFallbackColormap; + columnPixelSpacing: number; + columns: number; + // (undocumented) + decodeTimeInMS?: number; + // (undocumented) + getCanvas: () => HTMLCanvasElement; + getPixelData: () => PixelDataTypedArray; + height: number; + imageId: string; + intercept: number; + invert: boolean; + isPreScaled?: boolean; + // (undocumented) + loadTimeInMS?: number; + // (undocumented) + maxPixelValue: number; + minPixelValue: number; + modalityLUT?: CPUFallbackLUT; + numComps: number; + photometricInterpretation?: string; + preScale?: { + scaled?: boolean; + scalingParameters?: { + modality?: string; + rescaleSlope?: number; + rescaleIntercept?: number; + suvbw?: number; + }; + }; + render?: ( + enabledElement: CPUFallbackEnabledElement, + invalidated: boolean + ) => unknown; + rgba: boolean; + rowPixelSpacing: number; + rows: number; + scaling?: { + PT?: { + // @TODO: Do these values exist? + SUVlbmFactor?: number; + SUVbsaFactor?: number; + // accessed in ProbeTool + suvbwToSuvlbm?: number; + suvbwToSuvbsa?: number; + }; + }; + // (undocumented) + sharedCacheKey?: string; + sizeInBytes: number; + sliceThickness?: number; + slope: number; + stats?: { + lastStoredPixelDataToCanvasImageDataTime?: number; + lastGetPixelDataTime?: number; + lastPutImageDataTime?: number; + lastLutGenerateTime?: number; + lastRenderedViewport?: unknown; + lastRenderTime?: number; + }; + voiLUT?: CPUFallbackLUT; + voiLUTFunction: string; + width: number; + windowCenter: number[] | number; + windowWidth: number[] | number; +} + +// @public +interface IImageCalibration { + aspect?: number; + // (undocumented) + columnPixelSpacing?: number; + rowPixelSpacing?: number; + scale?: number; + sequenceOfUltrasoundRegions?: Record[]; + tooltip?: string; + type: CalibrationTypes; +} + +// @public +interface IImageData { + // (undocumented) + calibration?: IImageCalibration; + dimensions: Point3; + direction: Mat3; + hasPixelSpacing?: boolean; + imageData: vtkImageData; + metadata: { Modality: string }; + origin: Point3; + preScale?: { + scaled?: boolean; + scalingParameters?: { + modality?: string; + rescaleSlope?: number; + rescaleIntercept?: number; + suvbw?: number; + }; + }; + scalarData: Float32Array | Uint16Array | Uint8Array | Int16Array; + scaling?: Scaling; + spacing: Point3; +} + +// @public +interface IImageLoadObject { + cancelFn?: () => void; + decache?: () => void; + promise: Promise; +} + +// @public +interface IImageVolume { + // (undocumented) + cancelLoading?: () => void; + convertToCornerstoneImage?: ( + imageId: string, + imageIdIndex: number + ) => IImageLoadObject; + destroy(): void; + dimensions: Point3; + direction: Mat3; + getImageIdIndex(imageId: string): number; + getImageURIIndex(imageURI: string): number; + getScalarData(): VolumeScalarData; + hasPixelSpacing: boolean; + imageData?: vtkImageData; + imageIds: Array; + isDynamicVolume(): boolean; + isPreScaled: boolean; + loadStatus?: Record; + metadata: Metadata; + numVoxels: number; + origin: Point3; + referencedVolumeId?: string; + scaling?: { + PT?: { + SUVlbmFactor?: number; + SUVbsaFactor?: number; + suvbwToSuvlbm?: number; + suvbwToSuvbsa?: number; + }; + }; + sizeInBytes?: number; + spacing: Point3; + readonly volumeId: string; + vtkOpenGLTexture: any; +} + +// @public (undocumented) +type ImageActor = vtkImageSlice; + +// @public +type ImageCacheImageAddedEvent = +CustomEvent_2; + +// @public +type ImageCacheImageAddedEventDetail = { + image: ICachedImage; +}; + +// @public +type ImageCacheImageRemovedEvent = +CustomEvent_2; + +// @public +type ImageCacheImageRemovedEventDetail = { + imageId: string; +}; + +// @public +type ImageLoadedEvent = CustomEvent_2; + +// @public +type ImageLoadedEventDetail = { + image: IImage; +}; + +// @public +type ImageLoadedFailedEvent = CustomEvent_2; + +// @public +type ImageLoadedFailedEventDetail = { + imageId: string; + error: unknown; +}; + +// @public +type ImageLoaderFn = ( +imageId: string, +options?: Record +) => { + promise: Promise>; + cancelFn?: () => void | undefined; + decache?: () => void | undefined; +}; + +// @public +type ImageLoadProgressEvent = CustomEvent_2; + +// @public +type ImageLoadProgressEventDetail = { + url: string; + imageId: string; + loaded: number; + total: number; + percent: number; +}; + +// @public (undocumented) +interface ImagePixelModule { + // (undocumented) + bitsAllocated: number; + // (undocumented) + bitsStored: number; + // (undocumented) + highBit: number; + // (undocumented) + modality: string; + // (undocumented) + photometricInterpretation: string; + // (undocumented) + pixelRepresentation: string; + // (undocumented) + samplesPerPixel: number; + // (undocumented) + voiLUTFunction: VOILUTFunctionType; + // (undocumented) + windowCenter: number | number[]; + // (undocumented) + windowWidth: number | number[]; +} + +// @public (undocumented) +interface ImagePlaneModule { + // (undocumented) + columnCosines?: Point3; + // (undocumented) + columnPixelSpacing?: number; + // (undocumented) + columns: number; + // (undocumented) + frameOfReferenceUID: string; + // (undocumented) + imageOrientationPatient?: Float32Array; + // (undocumented) + imagePositionPatient?: Point3; + // (undocumented) + pixelSpacing?: Point2; + // (undocumented) + rowCosines?: Point3; + // (undocumented) + rowPixelSpacing?: number; + // (undocumented) + rows: number; + // (undocumented) + sliceLocation?: number; + // (undocumented) + sliceThickness?: number; +} + +// @public +type ImageRenderedEvent = CustomEvent_2; + +// @public +type ImageRenderedEventDetail = { + element: HTMLDivElement; + viewportId: string; + renderingEngineId: string; + suppressEvents?: boolean; + viewportStatus: ViewportStatus; +}; + +// @public (undocumented) +type ImageSliceData = { + numberOfSlices: number; + imageIndex: number; +}; + +// @public +type ImageSpacingCalibratedEvent = +CustomEvent_2; + +// @public +type ImageSpacingCalibratedEventDetail = { + element: HTMLDivElement; + viewportId: string; + renderingEngineId: string; + imageId: string; + calibration: IImageCalibration; + imageData: vtkImageData; + worldToIndex: mat4; +}; + +// @public +type ImageVolumeLoadingCompletedEvent = +CustomEvent_2; + +// @public +type ImageVolumeLoadingCompletedEventDetail = { + volumeId: string; + FrameOfReferenceUID: string; +}; + +// @public +type ImageVolumeModifiedEvent = CustomEvent_2; + +// @public +type ImageVolumeModifiedEventDetail = { + imageVolume: IImageVolume; + FrameOfReferenceUID: string; +}; + +// @public +interface IRegisterImageLoader { + // (undocumented) + registerImageLoader: (scheme: string, imageLoader: ImageLoaderFn) => void; +} + +// @public (undocumented) +interface IRenderingEngine { + // (undocumented) + _debugRender(): void; + // (undocumented) + destroy(): void; + // (undocumented) + disableElement(viewportId: string): void; + // (undocumented) + enableElement(viewportInputEntry: PublicViewportInput): void; + // (undocumented) + fillCanvasWithBackgroundColor( + canvas: HTMLCanvasElement, + backgroundColor: [number, number, number] + ): void; + // (undocumented) + getStackViewports(): Array; + // (undocumented) + getViewport(id: string): IStackViewport | IVolumeViewport; + // (undocumented) + getViewports(): Array; + // (undocumented) + getVolumeViewports(): Array; + // (undocumented) + hasBeenDestroyed: boolean; + // (undocumented) + id: string; + // (undocumented) + offScreenCanvasContainer: any; + // (undocumented) + offscreenMultiRenderWindow: any; + // (undocumented) + render(): void; + // (undocumented) + renderFrameOfReference(FrameOfReferenceUID: string): void; + // (undocumented) + renderViewport(viewportId: string): void; + // (undocumented) + renderViewports(viewportIds: Array): void; + // (undocumented) + resize(immediate?: boolean, keepCamera?: boolean): void; + // (undocumented) + setViewports(viewports: Array): void; +} + +// @public +interface IStackViewport extends IViewport { + calibrateSpacing(imageId: string): void; + canvasToWorld: (canvasPos: Point2) => Point3; + clearDefaultProperties(imageId?: string): void; + customRenderViewportToCanvas: () => { + canvas: HTMLCanvasElement; + element: HTMLDivElement; + viewportId: string; + renderingEngineId: string; + }; + getCamera(): ICamera; + getCornerstoneImage: () => IImage; + getCurrentImageId: () => string; + getCurrentImageIdIndex: () => number; + getDefaultProperties: (imageId?: string) => StackViewportProperties; + getFrameOfReferenceUID: () => string; + getImageData(): IImageData | CPUIImageData; + getImageIds: () => string[]; + getProperties: () => StackViewportProperties; + getRenderer(): any; + hasImageId: (imageId: string) => boolean; + hasImageURI: (imageURI: string) => boolean; + // (undocumented) + modality: string; + resetCamera(resetPan?: boolean, resetZoom?: boolean): boolean; + resetProperties(): void; + resetToDefaultProperties(): void; + resize: () => void; + scaling: Scaling; + setCamera(cameraInterface: ICamera): void; + setDefaultProperties( + ViewportProperties: StackViewportProperties, + imageId?: string + ): void; + setImageIdIndex(imageIdIndex: number): Promise; + setProperties( + { + voiRange, + invert, + interpolationType, + rotation, + colormap, + }: StackViewportProperties, + suppressEvents?: boolean + ): void; + setStack( + imageIds: Array, + currentImageIdIndex?: number + ): Promise; + unsetColormap(): void; + worldToCanvas: (worldPos: Point3) => Point2; +} + +// @public +interface IStreamingImageVolume extends ImageVolume { + clearLoadCallbacks(): void; + convertToCornerstoneImage(imageId: string, imageIdIndex: number): any; + decache(completelyRemove: boolean): void; +} + +// @public (undocumented) +interface IStreamingVolumeProperties { + imageIds: Array; + + loadStatus: { + loaded: boolean; + loading: boolean; + cancelled: boolean; + cachedFrames: Array; + callbacks: Array<() => void>; + }; +} + +// @public +interface IViewport { + _actors: Map; + addActor(actorEntry: ActorEntry): void; + addActors(actors: Array): void; + canvas: HTMLCanvasElement; + canvasToWorld: (canvasPos: Point2) => Point3; + customRenderViewportToCanvas: () => unknown; + defaultOptions: any; + element: HTMLDivElement; + getActor(actorUID: string): ActorEntry; + getActorByIndex(index: number): ActorEntry; + getActors(): Array; + getActorUIDByIndex(index: number): string; + getCamera(): ICamera; + getCanvas(): HTMLCanvasElement; + // (undocumented) + _getCorners(bounds: Array): Array[]; + getDefaultActor(): ActorEntry; + getDisplayArea(): DisplayArea | undefined; + getFrameOfReferenceUID: () => string; + getPan(): Point2; + getRenderer(): void; + getRenderingEngine(): any; + getRotation: () => number; + getZoom(): number; + id: string; + isDisabled: boolean; + options: ViewportInputOptions; + removeActors(actorUIDs: Array): void; + removeAllActors(): void; + render(): void; + renderingEngineId: string; + reset(immediate: boolean): void; + setActors(actors: Array): void; + setCamera(cameraInterface: ICamera, storeAsInitialCamera?: boolean): void; + setDisplayArea( + displayArea: DisplayArea, + callResetCamera?: boolean, + suppressEvents?: boolean + ); + setOptions(options: ViewportInputOptions, immediate: boolean): void; + setPan(pan: Point2, storeAsInitialCamera?: boolean); + setRendered(): void; + setZoom(zoom: number, storeAsInitialCamera?: boolean); + sHeight: number; + suppressEvents: boolean; + sWidth: number; + sx: number; + sy: number; + type: ViewportType; + // (undocumented) + updateRenderingPipeline: () => void; + viewportStatus: ViewportStatus; + worldToCanvas: (worldPos: Point3) => Point2; +} + +// @public +interface IViewportId { + // (undocumented) + renderingEngineId: string; + // (undocumented) + viewportId: string; +} + +// @public +interface IVolume { + dimensions: Point3; + direction: Mat3; + imageData?: vtkImageData; + metadata: Metadata; + origin: Point3; + referencedVolumeId?: string; + scalarData: VolumeScalarData | Array; + scaling?: { + PT?: { + // @TODO: Do these values exist? + SUVlbmFactor?: number; + SUVbsaFactor?: number; + // accessed in ProbeTool + suvbwToSuvlbm?: number; + suvbwToSuvbsa?: number; + }; + }; + sizeInBytes?: number; + spacing: Point3; + volumeId: string; +} + +// @public +interface IVolumeInput { + // (undocumented) + actorUID?: string; + // actorUID for segmentations, since two segmentations with the same volumeId + // can have different representations + blendMode?: BlendModes; + // actorUID for segmentations, since two segmentations with the same volumeId + // can have different representations + callback?: VolumeInputCallback; + // actorUID for segmentations, since two segmentations with the same volumeId + // can have different representations + slabThickness?: number; + // actorUID for segmentations, since two segmentations with the same volumeId + // can have different representations + visibility?: boolean; + // actorUID for segmentations, since two segmentations with the same volumeId + // can have different representations + volumeId: string; +} + +// @public +interface IVolumeLoadObject { + cancelFn?: () => void; + decache?: () => void; + promise: Promise; +} + +// @public +interface IVolumeViewport extends IViewport { + addVolumes( + volumeInputArray: Array, + immediate?: boolean, + suppressEvents?: boolean + ): Promise; + canvasToWorld: (canvasPos: Point2) => Point3; + clearDefaultProperties(volumeId?: string): void; + flip(flipDirection: FlipDirection): void; + getBounds(): any; + getCurrentImageId: () => string; + getCurrentImageIdIndex: () => number; + getDefaultProperties: (volumeId?: string) => VolumeViewportProperties; + // (undocumented) + getFrameOfReferenceUID: () => string; + getImageData(volumeId?: string): IImageData | undefined; + getImageIds: (volumeId?: string) => string[]; + getIntensityFromWorld(point: Point3): number; + getProperties: (volumeId?: string) => VolumeViewportProperties; + getSlabThickness(): number; + hasImageURI: (imageURI: string) => boolean; + hasVolumeId: (volumeId: string) => boolean; + removeVolumeActors(actorUIDs: Array, immediate?: boolean): void; + resetCamera( + resetPan?: boolean, + resetZoom?: boolean, + resetToCenter?: boolean + ): boolean; + resetProperties(volumeId: string): void; + setBlendMode( + blendMode: BlendModes, + filterActorUIDs?: Array, + immediate?: boolean + ): void; + setDefaultProperties( + ViewportProperties: VolumeViewportProperties, + volumeId?: string + ): void; + // (undocumented) + setOrientation(orientation: OrientationAxis): void; + setProperties( + { voiRange }: VolumeViewportProperties, + volumeId?: string, + suppressEvents?: boolean + ): void; + setSlabThickness( + slabThickness: number, + filterActorUIDs?: Array + ): void; + setVolumes( + volumeInputArray: Array, + immediate?: boolean, + suppressEvents?: boolean + ): Promise; + // (undocumented) + useCPURendering: boolean; + worldToCanvas: (worldPos: Point3) => Point2; +} + +// @public (undocumented) +function makeVolumeMetadata(niftiHeader: any, orientation: any, scalarData: any): Types.Metadata; + +// @public +type Mat3 = +| [number, number, number, number, number, number, number, number, number] +| Float32Array; + +// @public +type Metadata = { + BitsAllocated: number; + BitsStored: number; + SamplesPerPixel: number; + HighBit: number; + PhotometricInterpretation: string; + PixelRepresentation: number; + Modality: string; + SeriesInstanceUID?: string; + ImageOrientationPatient: Array; + PixelSpacing: Array; + FrameOfReferenceUID: string; + Columns: number; + Rows: number; + voiLut: Array; + VOILUTFunction: string; +}; + +// @public (undocumented) +function modalityScaleNifti(array: Float32Array | Int16Array | Uint8Array, niftiHeader: any): void; + +// @public (undocumented) +export class NiftiImageVolume extends ImageVolume { + constructor(imageVolumeProperties: Types.IVolume, streamingProperties: NiftiImageProperties); + // (undocumented) + cancelLoading: () => void; + // (undocumented) + clearLoadCallbacks(): void; + // (undocumented) + controller: AbortController; + // (undocumented) + decache(): void; + // (undocumented) + load: (callback: (...args: unknown[]) => void, priority?: number) => void; + // (undocumented) + loadStatus: LoadStatus; +} + +// @public +type OrientationVectors = { + viewPlaneNormal: Point3; + viewUp: Point3; +}; + +// @public (undocumented) +type PixelDataTypedArray = +| Float32Array +| Int16Array +| Uint16Array +| Uint8Array +| Int8Array +| Uint8ClampedArray; + +// @public +type Plane = [number, number, number, number]; + +// @public +type Point2 = [number, number]; + +// @public +type Point3 = [number, number, number]; + +// @public +type Point4 = [number, number, number, number]; + +// @public +type PreStackNewImageEvent = CustomEvent_2; + +// @public +type PreStackNewImageEventDetail = { + imageId: string; + imageIdIndex: number; + viewportId: string; + renderingEngineId: string; +}; + +// @public (undocumented) +type PTScaling = { + suvbwToSuvlbm?: number; + suvbwToSuvbsa?: number; + suvbw?: number; + suvlbm?: number; + suvbsa?: number; +}; + +// @public (undocumented) +type PublicContourSetData = ContourSetData; + +// @public (undocumented) +type PublicSurfaceData = { + id: string; + data: SurfaceData; + frameOfReferenceUID: string; + color?: Point3; +}; + +// @public +type PublicViewportInput = { + element: HTMLDivElement; + viewportId: string; + type: ViewportType; + defaultOptions?: ViewportInputOptions; +}; + +// @public +type RGB = [number, number, number]; + +// @public (undocumented) +type Scaling = { + PT?: PTScaling; +}; + +// @public (undocumented) +type ScalingParameters = { + rescaleSlope: number; + rescaleIntercept: number; + modality: string; + suvbw?: number; + suvlbm?: number; + suvbsa?: number; +}; + +// @public +type StackNewImageEvent = CustomEvent_2; + +// @public +type StackNewImageEventDetail = { + image: IImage; + imageId: string; + imageIdIndex: number; + viewportId: string; + renderingEngineId: string; +}; + +// @public +type StackViewportNewStackEvent = +CustomEvent_2; + +// @public +type StackViewportNewStackEventDetail = { + imageIds: string[]; + viewportId: string; + element: HTMLDivElement; + currentImageIdIndex: number; +}; + +// @public +type StackViewportProperties = ViewportProperties & { + interpolationType?: InterpolationType; + rotation?: number; + suppressEvents?: boolean; + isComputedVOI?: boolean; +}; + +// @public (undocumented) +type StackViewportScrollEvent = CustomEvent_2; + +// @public +type StackViewportScrollEventDetail = { + newImageIdIndex: number; + imageId: string; + direction: number; +}; + +// @public (undocumented) +type SurfaceData = { + points: number[]; + polys: number[]; +}; + +// @public +type TransformMatrix2D = [number, number, number, number, number, number]; + +// @public +type ViewportInputOptions = { + background?: RGB; + orientation?: OrientationAxis | OrientationVectors; + displayArea?: DisplayArea; + suppressEvents?: boolean; + parallelProjection?: boolean; +}; + +// @public (undocumented) +interface ViewportPreset { + // (undocumented) + ambient: string; + // (undocumented) + colorTransfer: string; + // (undocumented) + diffuse: string; + // (undocumented) + gradientOpacity: string; + // (undocumented) + interpolation: string; + // (undocumented) + name: string; + // (undocumented) + scalarOpacity: string; + // (undocumented) + shade: string; + // (undocumented) + specular: string; + // (undocumented) + specularPower: string; +} + +// @public +type ViewportProperties = { + voiRange?: VOIRange; + VOILUTFunction?: VOILUTFunctionType; + invert?: boolean; + colormap?: ColormapPublic; + interpolationType?: InterpolationType; +}; + +// @public (undocumented) +type VOI = { + windowWidth: number; + windowCenter: number; +}; + +// @public +type VoiModifiedEvent = CustomEvent_2; + +// @public +type VoiModifiedEventDetail = { + viewportId: string; + range: VOIRange; + volumeId?: string; + VOILUTFunction?: VOILUTFunctionType; + invert?: boolean; + invertStateChanged?: boolean; +}; + +// @public (undocumented) +type VOIRange = { + upper: number; + lower: number; +}; + +// @public (undocumented) +type VolumeActor = vtkVolume; + +// @public +type VolumeCacheVolumeAddedEvent = +CustomEvent_2; + +// @public +type VolumeCacheVolumeAddedEventDetail = { + volume: ICachedVolume; +}; + +// @public +type VolumeCacheVolumeRemovedEvent = +CustomEvent_2; + +// @public +type VolumeCacheVolumeRemovedEventDetail = { + volumeId: string; +}; + +// @public +type VolumeInputCallback = (params: { + volumeActor: VolumeActor; + volumeId: string; +}) => unknown; + +// @public +type VolumeLoadedEvent = CustomEvent_2; + +// @public +type VolumeLoadedEventDetail = { + volume: IImageVolume; +}; + +// @public +type VolumeLoadedFailedEvent = CustomEvent_2; + +// @public +type VolumeLoadedFailedEventDetail = { + volumeId: string; + error: unknown; +}; + +// @public +type VolumeLoaderFn = ( +volumeId: string, +options?: Record +) => { + promise: Promise>; + cancelFn?: () => void | undefined; + decache?: () => void | undefined; +}; + +// @public +type VolumeNewImageEvent = CustomEvent_2; + +// @public +type VolumeNewImageEventDetail = { + imageIndex: number; + numberOfSlices: number; + viewportId: string; + renderingEngineId: string; +}; + +// @public (undocumented) +type VolumeScalarData = Float32Array | Uint8Array | Uint16Array | Int16Array; + +// @public +type VolumeViewportProperties = ViewportProperties & { + preset?: string; + + slabThickness?: number; +}; + +// (No @packageDocumentation comment for this package) + +``` diff --git a/common/reviews/api/streaming-image-volume-loader.api.md b/common/reviews/api/streaming-image-volume-loader.api.md index 55e55ea992..d9dee22e78 100644 --- a/common/reviews/api/streaming-image-volume-loader.api.md +++ b/common/reviews/api/streaming-image-volume-loader.api.md @@ -924,6 +924,9 @@ interface IImageVolume { vtkOpenGLTexture: any; } +// @public (undocumented) +type ImageActor = vtkImageSlice; + // @public type ImageCacheImageAddedEvent = CustomEvent_2; diff --git a/common/reviews/api/tools.api.md b/common/reviews/api/tools.api.md index 02b1cbf1f6..9d54ed3a1f 100644 --- a/common/reviews/api/tools.api.md +++ b/common/reviews/api/tools.api.md @@ -6,6 +6,7 @@ import { Corners } from '@kitware/vtk.js/Interaction/Widgets/OrientationMarkerWidget/Constants'; import type { GetGPUTier } from 'detect-gpu'; +import { IColorMapPreset } from '@kitware/vtk.js/Rendering/Core/ColorTransferFunction/ColorMaps'; import type { mat4 } from 'gl-matrix'; import type { TierResult } from 'detect-gpu'; import type vtkActor from '@kitware/vtk.js/Rendering/Core/Actor'; @@ -1026,6 +1027,110 @@ declare namespace color { } } +// @public (undocumented) +class Colorbar extends Widget { + constructor(props: ColorbarProps); + // (undocumented) + get activeColormapName(): string; + set activeColormapName(colormapName: string); + // (undocumented) + protected createRootElement(): HTMLElement; + // (undocumented) + _createTicksBar(props: ColorbarProps): ColorbarTicks; + // (undocumented) + destroy(): void; + // (undocumented) + protected getVOIMultipliers(): [number, number]; + // (undocumented) + protected hideTicks(): void; + // (undocumented) + get imageRange(): ColorbarVOIRange; + set imageRange(imageRange: ColorbarVOIRange); + // (undocumented) + protected onContainerResize(): void; + // (undocumented) + protected onVoiChange(voiRange: ColorbarVOIRange): void; + // (undocumented) + get showFullImageRange(): boolean; + set showFullImageRange(value: boolean); + // (undocumented) + protected showTicks(): void; + // (undocumented) + get voiRange(): ColorbarVOIRange; + set voiRange(voiRange: ColorbarVOIRange); +} + +declare namespace colorbar { + export { + Types_3 as Types, + Enums_2 as Enums, + Colorbar, + ViewportColorbar + } +} + +// @public (undocumented) +type ColorbarCommonProps = { + imageRange?: ColorbarImageRange; + voiRange?: ColorbarVOIRange; + ticks?: { + position?: ColorbarRangeTextPosition; + style?: ColorbarTicksStyle; + }; + showFullPixelValueRange?: boolean; +}; + +// @public (undocumented) +type ColorbarImageRange = { + lower: number; + upper: number; +}; + +// @public (undocumented) +type ColorbarProps = (WidgetProps & ColorbarCommonProps) & { + colormaps: IColorMapPreset[]; + activeColormapName?: string; +}; + +// @public (undocumented) +enum ColorbarRangeTextPosition { + // (undocumented) + Bottom = "bottom", + // (undocumented) + Left = "left", + // (undocumented) + Right = "right", + // (undocumented) + Top = "top" +} + +// @public (undocumented) +type ColorbarSize = { + width: number; + height: number; +}; + +// @public (undocumented) +type ColorbarTicksProps = ColorbarCommonProps & { + top?: number; + left?: number; + size?: ColorbarSize; + container?: HTMLElement; +}; + +// @public (undocumented) +type ColorbarTicksStyle = { + font?: string; + color?: string; + tickSize?: number; + tickWidth?: number; + labelMargin?: number; + maxNumTicks?: number; +}; + +// @public (undocumented) +type ColorbarVOIRange = ColorbarImageRange; + // @public (undocumented) type ColorLUT = Array; @@ -1875,6 +1980,12 @@ declare namespace Enums { } export { Enums } +declare namespace Enums_2 { + export { + ColorbarRangeTextPosition + } +} + // @public (undocumented) enum Events { // (undocumented) @@ -2747,6 +2858,9 @@ interface IImageVolume { vtkOpenGLTexture: any; } +// @public (undocumented) +type ImageActor = vtkImageSlice; + // @public type ImageCacheImageAddedEvent = CustomEvent_2; @@ -5770,6 +5884,19 @@ declare namespace Types { } export { Types } +declare namespace Types_3 { + export { + ColorbarCommonProps, + ColorbarProps, + ColorbarImageRange, + ColorbarVOIRange, + ColorbarSize, + ColorbarTicksProps, + ColorbarTicksStyle, + ViewportColorbarProps + } +} + // @public (undocumented) function unlockAllAnnotations(): void; @@ -5806,7 +5933,8 @@ declare namespace utilities { scroll_2 as scroll, roundNumber, pointToString, - polyDataUtils + polyDataUtils, + voi } } export { utilities } @@ -5826,6 +5954,25 @@ declare namespace viewport { } } +// @public (undocumented) +class ViewportColorbar extends Colorbar { + constructor(props: ViewportColorbarProps); + // (undocumented) + get element(): HTMLDivElement; + // (undocumented) + get enabledElement(): Types_2.IEnabledElement; + // (undocumented) + protected getVOIMultipliers(): [number, number]; + // (undocumented) + protected onVoiChange(voiRange: ColorbarVOIRange): void; +} + +// @public (undocumented) +type ViewportColorbarProps = ColorbarProps & { + element: HTMLDivElement; + volumeId?: string; +}; + declare namespace viewportFilters { export { filterViewportsWithToolEnabled, @@ -5901,6 +6048,12 @@ type VOI = { windowCenter: number; }; +declare namespace voi { + export { + colorbar + } +} + // @public type VoiModifiedEvent = CustomEvent_2;