* init * more shifting * Move @babel dependencies up to workspace root * Begin updating dependent libraries to use sync'd webpack builds w/ watches * Commit changes before a long weekend * Split packages into platform and extensions * Consolidate how/where we specify file/module type entrypoints * Make it possible to pass in extensions as App props * Resolve viewer's modules * Target for UMD bundle * Add Webpack Stylus loader * Provide WebPack build options for microscopy, vtk, and ui * Better entrypoint for extensions * Push changes up to switch PCs * Fix ESM symlink build for Viewers * Shift docs to root * Specify additional lerna config props * Begin providing basic readme info * Rename example extension folder * Shift links to bottom of doc * First attempt at a netlify deploy preview * Attempt to fix deploy preview build * push workspace enabled to initial command; remove second yarn install; use npx to call gitbook cli commands * Try to use global gitbook-cli * Export node_modules path * More agressive with modifying PATH * Follow deploy log output advice * try again to set bin path * Try swapping bash with a new shell * Try bash -l instead of exec bash * Annd let's try again * and again * We may have figured it out johnny, boy * We need to pull cornerstone-wado-image-loader files from hoisted node_modules * Attempt to fix ticks/escapes * Change copy syntax; try running tests on viewers from root for circleci * Lower version to match circleci image * Try alternative jest-canvas-mock location and version file syntax * Run version command instead of calling node directly * Try alternative jest transform * Set path and enable workspaces * Add codecov flags * Use cpx so our copying finishes? * Get all platform unit tests to run * Run and report individually and in parallel * Try once relying on codecov yaml to split w/ flags * Add projects to split by flags * Generate separate reports * Trigger codecov after everything has finished running; these may not support flags * Use recommended flags from issue comments for codecov * Fix typo * Remove clear flag * Shift back to calling codecov from root * Generate example for codecov issue * Try fixing paths * Simplify * Try using codecov orb * Bump circleci version * Upload core and viewer * Use full string paths * Remove individual codecov calls * Fix dir * Combine files before upload * Escape anchors * tryfix syntax * Escape string literal * Combine lines to reduce path * long paths * Use home alias * Fix filename * Try to see the contents of our cat'd file * Also upload `core` * Try running with aliased folder * Fix path; split PR and Merge unit tests into separate jobs * Set default threshold * Fix typo * Changing to scoped package names * Adding note * Catch more updates * chore(release): publish %s [skip ci] - @ohif/extension-cornerstone@0.0.39-alpha.0 - @ohif/extension-dicom-html@0.0.4-alpha.0 - @ohif/extension-dicom-microscopy@0.0.9-alpha.0 - @ohif/extension-dicom-pdf@0.0.8-alpha.0 - @ohif/extension-vtk@0.1.4-alpha.0 - @ohif/core@0.11.1-alpha.0 - @ohif/i18n@0.2.3-alpha.0 - @ohif/ui@0.2.18-alpha.0 - @ohif/viewer@0.0.22-alpha.0 * Clean up primary readme * Support for dev and dev:* commands * Shift build command; satisfy default PWA build * Remove old scripts * Clean up UI to set webpack scripts * Tidy up project links * Clean duplicate code in UI project * Update UI project's docs * Clean up ui and i18n config * Reduce duplicate code in `@ohif/core` * Fix @ohif/ui versions * bump @ohif/core versions * Redux testkit dep * Remove unused/duplicate config files for projects/packages * ohif-core --> @ohif/core * Safer publish command * chore(release): publish %s [skip ci] - @ohif/extension-cornerstone@0.0.39-alpha.1 - @ohif/extension-dicom-html@0.0.4-alpha.1 - @ohif/extension-dicom-microscopy@0.0.9-alpha.1 - @ohif/extension-dicom-pdf@0.0.8-alpha.1 - @ohif/extension-vtk@0.1.4-alpha.1 - @ohif/core@0.11.1-alpha.1 - @ohif/i18n@0.2.3-alpha.1 - @ohif/ui@0.2.18-alpha.1 - @ohif/viewer@0.0.22-alpha.1 * test version bump * Add global prettier config * chore(release): publish %s [skip ci] - @ohif/viewer@0.0.22-alpha.2 * test lerna publish * Update package.jsons * Fix build scripts * catch changes * chore(release): publish %s [skip ci] - @ohif/extension-cornerstone@0.0.39-alpha.2 - @ohif/extension-dicom-html@0.0.4-alpha.2 - @ohif/extension-dicom-microscopy@0.0.9-alpha.2 - @ohif/extension-dicom-pdf@0.0.8-alpha.2 - @ohif/extension-vtk@0.1.4-alpha.2 - @ohif/core@0.11.1-alpha.2 - @ohif/i18n@0.2.3-alpha.2 - @ohif/ui@0.2.18-alpha.2 - @ohif/viewer@0.0.22-alpha.3 * fix eslint issues caused by eslint 6 and our react-app config * Publish with canary tag * chore(release): publish %s [skip ci] - @ohif/extension-cornerstone@0.0.39-alpha.3 - @ohif/extension-dicom-html@0.0.4-alpha.3 - @ohif/extension-dicom-microscopy@0.0.9-alpha.3 - @ohif/extension-dicom-pdf@0.0.8-alpha.3 - @ohif/extension-vtk@0.1.4-alpha.3 - @ohif/core@0.11.1-alpha.3 - @ohif/i18n@0.2.3-alpha.3 - @ohif/ui@0.2.18-alpha.3 - @ohif/viewer@0.0.22-alpha.4 * chore(release): publish %s [skip ci] - @ohif/extension-cornerstone@0.0.39-alpha.4 - @ohif/extension-dicom-html@0.0.4-alpha.4 - @ohif/extension-dicom-microscopy@0.0.9-alpha.4 - @ohif/extension-dicom-pdf@0.0.8-alpha.4 - @ohif/extension-vtk@0.1.4-alpha.4 - @ohif/core@0.11.1-alpha.4 - @ohif/i18n@0.2.3-alpha.4 - @ohif/ui@0.2.18-alpha.4 - @ohif/viewer@0.0.22-alpha.5 * Simplify build * chore(release): publish %s [skip ci] - @ohif/extension-cornerstone@0.0.39-alpha.5 - @ohif/extension-dicom-html@0.0.4-alpha.5 - @ohif/extension-dicom-microscopy@0.0.9-alpha.5 - @ohif/extension-dicom-pdf@0.0.8-alpha.5 - @ohif/extension-vtk@0.1.4-alpha.5 - @ohif/core@0.11.1-alpha.5 - @ohif/i18n@0.2.3-alpha.5 - @ohif/ui@0.2.18-alpha.5 - @ohif/viewer@0.0.22-alpha.6 * chore(release): publish %s [skip ci] - @ohif/extension-cornerstone@0.0.39-alpha.6 - @ohif/extension-dicom-html@0.0.4-alpha.6 - @ohif/extension-dicom-microscopy@0.0.9-alpha.6 - @ohif/extension-dicom-pdf@0.0.8-alpha.6 - @ohif/extension-vtk@0.1.4-alpha.6 - @ohif/core@0.11.1-alpha.6 - @ohif/i18n@0.2.3-alpha.6 - @ohif/ui@0.2.18-alpha.6 - @ohif/viewer@0.0.22-alpha.7 * Split version and publish * chore(release): publish %s [skip ci] - @ohif/extension-cornerstone@0.0.39-alpha.7 - @ohif/extension-dicom-html@0.0.4-alpha.7 - @ohif/extension-dicom-microscopy@0.0.9-alpha.7 - @ohif/extension-dicom-pdf@0.0.8-alpha.7 - @ohif/extension-vtk@0.1.4-alpha.7 - @ohif/core@0.11.1-alpha.7 - @ohif/i18n@0.2.3-alpha.7 - @ohif/ui@0.2.18-alpha.7 - @ohif/viewer@0.0.22-alpha.8 * Try simplify netlify build * react-viewerbase --> @ohif/ui * add test for stylus loader * Try new way to specify publish path * Use an output folder that won't be ignored * Try move command * fiddle with paths * Dig for some information * Make sure we're in root for our commands * Remove discovery * Setup script-tag example * Fix typo * Move config folder * Update cornerstone extension to use shared/root webpack configs * capture changes * Update extension webpack configs * Don't check in docz output * Fix babel runtime resolution issue * Simplify ui build steps * Push in progress * Shift directories * Get viewer dev path to work * Fix viewer commonjs build * Fix build pwa config for viewers * Make sure our package specific command can be called from root * Update notes * Fix imports and misc. merge notes * Bump wado-image-loader version * Switch up commands to remove webworkers copy * Fix CI config * Fix more broken package references * Add react-dropzone * Persist so e2e test can use * Fix lockfile * Use cypress orb to simplify CI * Fix config * Try again * Install yarn latest for orb * Install yarn a different way * Wait on correct port * Run tests in viewer project * Fix cypress config * Make Cypress happier - use default location * Simplify e2e testing * Capture UI changes since fork * Catch core up * Grab latest deps after merge * Fix broken import * Make sure cornerstone-wado-image-loader resolves to 3+ * Note regarding orbs * Fix refresh on 404 routes * Fix odd error for cornerstone-wado-image-loader * Different target for UMD * Simplify dev script * Try to get fancy with netlify output * Let's do it again * Try to fix measurementTools issue * See if we can get our lerna magic to work on merge to monorepo branch
392 lines
11 KiB
JavaScript
392 lines
11 KiB
JavaScript
import {
|
|
vtkInteractorStyleMPRCrosshairs,
|
|
vtkInteractorStyleMPRWindowLevel,
|
|
vtkInteractorStyleMPRSlice,
|
|
vtkSVGCrosshairsWidget,
|
|
vtkSVGWidgetManager,
|
|
} from 'react-vtkjs-viewport';
|
|
|
|
import setMPRLayout from './utils/setMPRLayout.js';
|
|
import setViewportToVTK from './utils/setViewportToVTK.js';
|
|
import vtkCoordinate from 'vtk.js/Sources/Rendering/Core/Coordinate';
|
|
import vtkMath from 'vtk.js/Sources/Common/Core/Math';
|
|
import vtkMatrixBuilder from 'vtk.js/Sources/Common/Core/MatrixBuilder';
|
|
import Constants from 'vtk.js/Sources/Rendering/Core/VolumeMapper/Constants.js';
|
|
|
|
const { BlendMode } = Constants;
|
|
|
|
// TODO: Put this somewhere else
|
|
let apis = {};
|
|
let currentSlabThickness = 0.1;
|
|
|
|
function getCrosshairCallbackForIndex(index) {
|
|
return ({ worldPos }) => {
|
|
// Set camera focal point to world coordinate for linked views
|
|
apis.forEach((api, viewportIndex) => {
|
|
if (viewportIndex !== index) {
|
|
// We are basically doing the same as getSlice but with the world coordinate
|
|
// that we want to jump to instead of the camera focal point.
|
|
// I would rather do the camera adjustment directly but I keep
|
|
// doing it wrong and so this is good enough for now.
|
|
const renderWindow = api.genericRenderWindow.getRenderWindow();
|
|
|
|
const istyle = renderWindow.getInteractor().getInteractorStyle();
|
|
const sliceNormal = istyle.getSliceNormal();
|
|
const transform = vtkMatrixBuilder
|
|
.buildFromDegree()
|
|
.identity()
|
|
.rotateFromDirections(sliceNormal, [1, 0, 0]);
|
|
|
|
const mutatedWorldPos = worldPos.slice();
|
|
transform.apply(mutatedWorldPos);
|
|
const slice = mutatedWorldPos[0];
|
|
|
|
istyle.setSlice(slice);
|
|
|
|
renderWindow.render();
|
|
}
|
|
|
|
const renderer = api.genericRenderWindow.getRenderer();
|
|
const wPos = vtkCoordinate.newInstance();
|
|
wPos.setCoordinateSystemToWorld();
|
|
wPos.setValue(worldPos);
|
|
|
|
const displayPosition = wPos.getComputedDisplayValue(renderer);
|
|
const { svgWidgetManager } = api;
|
|
api.svgWidgets.crosshairsWidget.setPoint(
|
|
displayPosition[0],
|
|
displayPosition[1]
|
|
);
|
|
svgWidgetManager.render();
|
|
});
|
|
};
|
|
}
|
|
|
|
async function _getActiveViewportVTKApi(viewports) {
|
|
const { layout, viewportSpecificData, activeViewportIndex } = viewports;
|
|
|
|
const currentData = layout.viewports[activeViewportIndex];
|
|
if (currentData && currentData.plugin === 'vtk') {
|
|
// TODO: I was storing/pulling this from Redux but ran into weird issues
|
|
if (apis[activeViewportIndex]) {
|
|
return apis[activeViewportIndex];
|
|
}
|
|
}
|
|
|
|
const displaySet = viewportSpecificData[activeViewportIndex];
|
|
|
|
let api;
|
|
if (!api) {
|
|
try {
|
|
api = await setViewportToVTK(
|
|
displaySet,
|
|
activeViewportIndex,
|
|
layout,
|
|
viewportSpecificData
|
|
);
|
|
} catch (error) {
|
|
throw new Error(error);
|
|
}
|
|
}
|
|
|
|
return api;
|
|
}
|
|
|
|
function _setView(api, sliceNormal, viewUp) {
|
|
const renderWindow = api.genericRenderWindow.getRenderWindow();
|
|
const renderer = api.genericRenderWindow.getRenderer();
|
|
const camera = renderer.getActiveCamera();
|
|
const istyle = renderWindow.getInteractor().getInteractorStyle();
|
|
istyle.setSliceNormal(...sliceNormal);
|
|
camera.setViewUp(...viewUp);
|
|
|
|
renderWindow.render();
|
|
}
|
|
|
|
function switchMPRInteractors(api, istyle) {
|
|
const renderWindow = api.genericRenderWindow.getRenderWindow();
|
|
const renderer = api.genericRenderWindow.getRenderer();
|
|
const camera = renderer.getActiveCamera();
|
|
const currentIStyle = renderWindow.getInteractor().getInteractorStyle();
|
|
|
|
let currentNormal;
|
|
if (currentIStyle.getSliceNormal && istyle.getSliceNormal) {
|
|
currentNormal = currentIStyle.getSliceNormal();
|
|
}
|
|
|
|
let currentSlabThickness;
|
|
if (currentIStyle.getSlabThickness && istyle.getSlabThickness) {
|
|
currentSlabThickness = currentIStyle.getSlabThickness();
|
|
}
|
|
|
|
renderWindow.getInteractor().setInteractorStyle(istyle);
|
|
|
|
// TODO: Not sure why this is required the second time this function is called
|
|
istyle.setInteractor(renderWindow.getInteractor());
|
|
|
|
if (istyle.getVolumeMapper() !== api.volumes[0]) {
|
|
if (currentNormal) {
|
|
istyle.setSliceNormal(currentNormal);
|
|
}
|
|
|
|
if (currentSlabThickness) {
|
|
istyle.setSlabThickness(currentSlabThickness);
|
|
}
|
|
|
|
istyle.setVolumeMapper(api.volumes[0]);
|
|
}
|
|
}
|
|
|
|
const actions = {
|
|
axial: async ({ viewports }) => {
|
|
const api = await _getActiveViewportVTKApi(viewports);
|
|
|
|
apis[viewports.activeViewportIndex] = api;
|
|
|
|
_setView(api, [0, 0, 1], [0, -1, 0]);
|
|
},
|
|
sagittal: async ({ viewports }) => {
|
|
const api = await _getActiveViewportVTKApi(viewports);
|
|
|
|
apis[viewports.activeViewportIndex] = api;
|
|
|
|
_setView(api, [1, 0, 0], [0, 0, 1]);
|
|
},
|
|
coronal: async ({ viewports }) => {
|
|
const api = await _getActiveViewportVTKApi(viewports);
|
|
|
|
apis[viewports.activeViewportIndex] = api;
|
|
|
|
_setView(api, [0, 1, 0], [0, 0, 1]);
|
|
},
|
|
enableRotateTool: () => {
|
|
apis.forEach(api => {
|
|
const istyle = vtkInteractorStyleMPRSlice.newInstance();
|
|
|
|
switchMPRInteractors(api, istyle);
|
|
});
|
|
},
|
|
enableCrosshairsTool: () => {
|
|
apis.forEach((api, index) => {
|
|
const istyle = vtkInteractorStyleMPRCrosshairs.newInstance();
|
|
|
|
switchMPRInteractors(api, istyle);
|
|
|
|
istyle.setCallback(getCrosshairCallbackForIndex(index));
|
|
});
|
|
},
|
|
enableLevelTool: () => {
|
|
apis.forEach(api => {
|
|
const istyle = vtkInteractorStyleMPRWindowLevel.newInstance();
|
|
|
|
switchMPRInteractors(api, istyle);
|
|
});
|
|
},
|
|
setSlabThickness: slabThickness => {
|
|
currentSlabThickness = slabThickness;
|
|
|
|
apis.forEach(api => {
|
|
const renderWindow = api.genericRenderWindow.getRenderWindow();
|
|
const istyle = renderWindow.getInteractor().getInteractorStyle();
|
|
|
|
if (istyle.setSlabThickness) {
|
|
istyle.setSlabThickness(currentSlabThickness);
|
|
|
|
// TODO: Do this inside the interactors in a setSlabThickness function instead
|
|
const renderer = api.genericRenderWindow.getRenderer();
|
|
const camera = renderer.getActiveCamera();
|
|
const dist = camera.getDistance();
|
|
const near = dist - currentSlabThickness / 2;
|
|
const far = dist + currentSlabThickness / 2;
|
|
|
|
camera.setClippingRange(near, far);
|
|
}
|
|
|
|
renderWindow.render();
|
|
});
|
|
},
|
|
changeSlabThickness: ({ change }) => {
|
|
currentSlabThickness += change;
|
|
currentSlabThickness = Math.max(currentSlabThickness, 0.1);
|
|
|
|
apis.forEach(api => {
|
|
const renderWindow = api.genericRenderWindow.getRenderWindow();
|
|
const istyle = renderWindow.getInteractor().getInteractorStyle();
|
|
|
|
if (istyle.setSlabThickness) {
|
|
istyle.setSlabThickness(currentSlabThickness);
|
|
}
|
|
|
|
renderWindow.render();
|
|
});
|
|
},
|
|
setBlendMode: ({ blendMode }) => {
|
|
apis.forEach(api => {
|
|
const renderWindow = api.genericRenderWindow.getRenderWindow();
|
|
const istyle = renderWindow.getInteractor().getInteractorStyle();
|
|
|
|
api.volumes[0].getMapper().setBlendMode(blendMode);
|
|
|
|
renderWindow.render();
|
|
});
|
|
},
|
|
mpr2d: async ({ viewports }) => {
|
|
const displaySet =
|
|
viewports.viewportSpecificData[viewports.activeViewportIndex];
|
|
|
|
let apiByViewport;
|
|
try {
|
|
apiByViewport = await setMPRLayout(displaySet);
|
|
} catch (error) {
|
|
throw new Error(error);
|
|
}
|
|
|
|
apis = apiByViewport;
|
|
|
|
/*const rgbTransferFunction = apiByViewport[0].volumes[0]
|
|
.getProperty()
|
|
.getRGBTransferFunction(0);
|
|
rgbTransferFunction.onModified(() => {
|
|
apiByViewport.forEach(a => {
|
|
const renderWindow = a.genericRenderWindow.getRenderWindow();
|
|
|
|
renderWindow.render();
|
|
});
|
|
});*/
|
|
|
|
apiByViewport.forEach((api, index) => {
|
|
const renderWindow = api.genericRenderWindow.getRenderWindow();
|
|
const renderer = api.genericRenderWindow.getRenderer();
|
|
const camera = renderer.getActiveCamera();
|
|
|
|
const istyle = vtkInteractorStyleMPRCrosshairs.newInstance();
|
|
renderWindow.getInteractor().setInteractorStyle(istyle);
|
|
|
|
istyle.setVolumeMapper(api.volumes[0]);
|
|
istyle.setCallback(getCrosshairCallbackForIndex(index));
|
|
|
|
const svgWidgetManager = vtkSVGWidgetManager.newInstance();
|
|
svgWidgetManager.setRenderer(renderer);
|
|
svgWidgetManager.setScale(1);
|
|
|
|
const crosshairsWidget = vtkSVGCrosshairsWidget.newInstance();
|
|
|
|
svgWidgetManager.addWidget(crosshairsWidget);
|
|
svgWidgetManager.render();
|
|
|
|
api.svgWidgetManager = svgWidgetManager;
|
|
api.svgWidgets = {
|
|
crosshairsWidget,
|
|
};
|
|
|
|
switch (index) {
|
|
default:
|
|
case 0:
|
|
//Axial
|
|
istyle.setSliceNormal(0, 0, 1);
|
|
camera.setViewUp(0, -1, 0);
|
|
|
|
break;
|
|
case 1:
|
|
// sagittal
|
|
istyle.setSliceNormal(1, 0, 0);
|
|
camera.setViewUp(0, 0, 1);
|
|
break;
|
|
case 2:
|
|
// Coronal
|
|
istyle.setSliceNormal(0, 1, 0);
|
|
camera.setViewUp(0, 0, 1);
|
|
break;
|
|
}
|
|
|
|
renderWindow.render();
|
|
});
|
|
},
|
|
};
|
|
|
|
window.vtkActions = actions;
|
|
|
|
const definitions = {
|
|
axial: {
|
|
commandFn: actions.axial,
|
|
storeContexts: ['viewports'],
|
|
options: {},
|
|
},
|
|
coronal: {
|
|
commandFn: actions.coronal,
|
|
storeContexts: ['viewports'],
|
|
options: {},
|
|
},
|
|
sagittal: {
|
|
commandFn: actions.sagittal,
|
|
storeContexts: ['viewports'],
|
|
options: {},
|
|
},
|
|
enableRotateTool: {
|
|
commandFn: actions.enableRotateTool,
|
|
storeContexts: [],
|
|
options: {},
|
|
},
|
|
enableCrosshairsTool: {
|
|
commandFn: actions.enableCrosshairsTool,
|
|
storeContexts: [],
|
|
options: {},
|
|
},
|
|
enableLevelTool: {
|
|
commandFn: actions.enableLevelTool,
|
|
storeContexts: [],
|
|
options: {},
|
|
},
|
|
setBlendModeToComposite: {
|
|
commandFn: actions.setBlendMode,
|
|
storeContexts: [],
|
|
options: { blendMode: BlendMode.COMPOSITE_BLEND },
|
|
},
|
|
setBlendModeToMaximumIntensity: {
|
|
commandFn: actions.setBlendMode,
|
|
storeContexts: [],
|
|
options: { blendMode: BlendMode.MAXIMUM_INTENSITY_BLEND },
|
|
},
|
|
setBlendModeToMinimumIntensity: {
|
|
commandFn: actions.setBlendMode,
|
|
storeContexts: [],
|
|
options: { blendMode: BlendMode.MINIMUM_INTENSITY_BLEND },
|
|
},
|
|
setBlendModeToAverageIntensity: {
|
|
commandFn: actions.setBlendMode,
|
|
storeContexts: [],
|
|
options: { blendMode: BlendMode.AVERAGE_INTENSITY_BLEND },
|
|
},
|
|
setSlabThickness: {
|
|
// TODO: How do we pass in a function argument?
|
|
commandFn: actions.setSlabThickness,
|
|
storeContexts: [],
|
|
options: {},
|
|
},
|
|
increaseSlabThickness: {
|
|
commandFn: actions.changeSlabThickness,
|
|
storeContexts: [],
|
|
options: {
|
|
change: 3,
|
|
},
|
|
},
|
|
decreaseSlabThickness: {
|
|
commandFn: actions.changeSlabThickness,
|
|
storeContexts: [],
|
|
options: {
|
|
change: -3,
|
|
},
|
|
},
|
|
mpr2d: {
|
|
commandFn: actions.mpr2d,
|
|
storeContexts: ['viewports'],
|
|
options: {},
|
|
context: 'VIEWER',
|
|
},
|
|
};
|
|
|
|
export default {
|
|
definitions,
|
|
defaultContext: 'ACTIVE_VIEWPORT::VTK',
|
|
};
|