@metacell/geppetto-meta-ui
Version:
React components from geppetto-meta to create neuroscience applications and visualize data.
1,017 lines (990 loc) • 40.6 kB
JavaScript
function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
function _extends() { return _extends = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends.apply(null, arguments); }
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
function _toConsumableArray(r) { return _arrayWithoutHoles(r) || _iterableToArray(r) || _unsupportedIterableToArray(r) || _nonIterableSpread(); }
function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
function _iterableToArray(r) { if ("undefined" != typeof Symbol && null != r[Symbol.iterator] || null != r["@@iterator"]) return Array.from(r); }
function _arrayWithoutHoles(r) { if (Array.isArray(r)) return _arrayLikeToArray(r); }
function _createForOfIteratorHelper(r, e) { var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (!t) { if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e && r && "number" == typeof r.length) { t && (r = t); var _n = 0, F = function F() {}; return { s: F, n: function n() { return _n >= r.length ? { done: !0 } : { done: !1, value: r[_n++] }; }, e: function e(r) { throw r; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var o, a = !0, u = !1; return { s: function s() { t = t.call(r); }, n: function n() { var r = t.next(); return a = r.done, r; }, e: function e(r) { u = !0, o = r; }, f: function f() { try { a || null == t["return"] || t["return"](); } finally { if (u) throw o; } } }; }
function _slicedToArray(r, e) { return _arrayWithHoles(r) || _iterableToArrayLimit(r, e) || _unsupportedIterableToArray(r, e) || _nonIterableRest(); }
function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
function _iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (null != t) { var e, n, i, u, a = [], f = !0, o = !1; try { if (i = (t = t.call(r)).next, 0 === l) { if (Object(t) !== t) return; f = !1; } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); } catch (r) { o = !0, n = r; } finally { try { if (!f && null != t["return"] && (u = t["return"](), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } }
function _arrayWithHoles(r) { if (Array.isArray(r)) return r; }
function _classCallCheck(a, n) { if (!(a instanceof n)) throw new TypeError("Cannot call a class as a function"); }
function _defineProperties(e, r) { for (var t = 0; t < r.length; t++) { var o = r[t]; o.enumerable = o.enumerable || !1, o.configurable = !0, "value" in o && (o.writable = !0), Object.defineProperty(e, _toPropertyKey(o.key), o); } }
function _createClass(e, r, t) { return r && _defineProperties(e.prototype, r), t && _defineProperties(e, t), Object.defineProperty(e, "prototype", { writable: !1 }), e; }
function _callSuper(t, o, e) { return o = _getPrototypeOf(o), _possibleConstructorReturn(t, _isNativeReflectConstruct() ? Reflect.construct(o, e || [], _getPrototypeOf(t).constructor) : o.apply(t, e)); }
function _possibleConstructorReturn(t, e) { if (e && ("object" == _typeof(e) || "function" == typeof e)) return e; if (void 0 !== e) throw new TypeError("Derived constructors may only return object or undefined"); return _assertThisInitialized(t); }
function _assertThisInitialized(e) { if (void 0 === e) throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); return e; }
function _isNativeReflectConstruct() { try { var t = !Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); } catch (t) {} return (_isNativeReflectConstruct = function _isNativeReflectConstruct() { return !!t; })(); }
function _getPrototypeOf(t) { return _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf.bind() : function (t) { return t.__proto__ || Object.getPrototypeOf(t); }, _getPrototypeOf(t); }
function _inherits(t, e) { if ("function" != typeof e && null !== e) throw new TypeError("Super expression must either be null or a function"); t.prototype = Object.create(e && e.prototype, { constructor: { value: t, writable: !0, configurable: !0 } }), Object.defineProperty(t, "prototype", { writable: !1 }), e && _setPrototypeOf(t, e); }
function _setPrototypeOf(t, e) { return _setPrototypeOf = Object.setPrototypeOf ? Object.setPrototypeOf.bind() : function (t, e) { return t.__proto__ = e, t; }, _setPrototypeOf(t, e); }
function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; }
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : i + ""; }
function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
import React, { Component } from 'react';
import { PropTypes } from 'prop-types';
import * as THREE from 'three-legacy';
import DicomViewerUtils from './DicomViewerUtils';
import { offset } from '../utilities';
import { boundingBoxHelperFactory, VolumeLoader, StackModel } from 'ami.js';
var HelpersBoundingBox = boundingBoxHelperFactory(THREE);
import { faDownload } from '@fortawesome/free-solid-svg-icons';
import CustomToolbar from '../common/CustomToolbar';
import { createZipFromRemoteFiles } from './util';
import Loader from "../loader/Loader";
var classes = {
dicomViewer: "dicom-viewer",
renderer: "dicom-viewer-renderer",
toolbar: "dicom-viewer-toolbar",
toolbarBox: "dicom-viewer-toolbar-box",
button: "dicom-viewer-button"
};
var _DicomViewer = /*#__PURE__*/function (_Component) {
function _DicomViewer(props) {
var _this2;
_classCallCheck(this, _DicomViewer);
_this2 = _callSuper(this, _DicomViewer, [props]);
// 3d renderer
_defineProperty(_this2, "animationOn", true);
_defineProperty(_this2, "animationSkipRate", 3);
_this2.r0 = {
domClass: 'r0',
domElement: null,
renderer: null,
color: 0x212121,
targetID: 0,
camera: null,
controls: null,
scene: null,
light: null,
parent: _this2
};
// 2d axial renderer
_this2.r1 = {
domClass: 'r1',
domElement: null,
renderer: null,
color: 0x121212,
sliceOrientation: 'axial',
sliceColor: 0xff1744,
targetID: 1,
camera: null,
controls: null,
scene: null,
light: null,
stackHelper: null,
localizerHelper: null,
localizerScene: null,
widgets: [],
parent: _this2
};
// 2d sagittal renderer
_this2.r2 = {
domClass: 'r2',
domElement: null,
renderer: null,
color: 0x121212,
sliceOrientation: 'sagittal',
sliceColor: 0xffea00,
targetID: 2,
camera: null,
controls: null,
scene: null,
light: null,
stackHelper: null,
localizerHelper: null,
localizerScene: null,
widgets: [],
parent: _this2
};
// 2d coronal renderer
_this2.r3 = {
domClass: 'r3',
domElement: null,
renderer: null,
color: 0x121212,
sliceOrientation: 'coronal',
sliceColor: 0x76ff03,
targetID: 3,
camera: null,
controls: null,
scene: null,
light: null,
stackHelper: null,
localizerHelper: null,
localizerScene: null,
widgets: [],
parent: _this2
};
_this2.state = {
ready: false
};
_this2.containerRef = /*#__PURE__*/React.createRef();
_this2.onRender = _this2.props.onRender;
_this2.drag = false;
_this2.animationSkipRate = _this2.props.animationSkipRate || 3;
_this2.startAnimation = _this2.startAnimation.bind(_this2);
_this2.stopAnimation = _this2.stopAnimation.bind(_this2);
_this2.centerOn = _this2.centerOn.bind(_this2);
_this2.resizeObserver = null;
return _this2;
}
_inherits(_DicomViewer, _Component);
return _createClass(_DicomViewer, [{
key: "extractFilesPath",
value: function extractFilesPath(data) {
if (!data) {
return undefined;
}
if (!data.getMetaType) {
return data;
}
if (data.getMetaType() === 'Instance') {
var value = data.getVariable().getInitialValues()[0].value;
switch (value.format) {
case 'NIFTI':
return value.data;
case 'DCM':
break;
// todo: what do we do here?
}
}
return undefined;
}
}, {
key: "loadModel",
value: function loadModel(modelData) {
var data = this.extractFilesPath(modelData);
if (!data) {
return;
}
this.ready = false;
var _this = this;
var animationCount = 0;
/**
* Init the view
*/
function init() {
/**
* Called on each animation frame
*/
function animate() {
if (_this.ready && _this.animationOn && animationCount++ % _this.animationSkipRate === 0) {
// we are ready when both meshes have been loaded
var _this$props = _this.props,
mode = _this$props.mode,
orientation = _this$props.orientation;
var shouldRenderPlane = function shouldRenderPlane(plane) {
return mode === 'quad_view' || orientation === plane;
};
var shouldRender3D = shouldRenderPlane('3d');
var planesSetup = {
'r1': 'sagittal',
'r2': 'axial',
'r3': 'coronal'
};
if (shouldRender3D) {
// render
_this.r0.controls.update();
_this.r0.light.position.copy(_this.r0.camera.position);
_this.r0.renderer.render(_this.r0.scene, _this.r0.camera);
_this.r0.scene.background = new THREE.Color("#353535");
}
for (var _i = 0, _Object$entries = Object.entries(planesSetup); _i < _Object$entries.length; _i++) {
var _Object$entries$_i = _slicedToArray(_Object$entries[_i], 2),
planeName = _Object$entries$_i[0],
planeOrientation = _Object$entries$_i[1];
if (!shouldRenderPlane(planeOrientation)) {
continue;
}
var plane = _this[planeName];
var camera = plane.camera;
var renderer = plane.renderer;
plane.controls.update();
// clear renderer
renderer.clear();
plane.renderer.render(plane.scene, camera);
plane.scene.background = new THREE.Color("#353535");
// localizer
renderer.clearDepth();
renderer.render(plane.localizerScene, camera);
// Widgets
var _iterator = _createForOfIteratorHelper(plane.widgets),
_step;
try {
for (_iterator.s(); !(_step = _iterator.n()).done;) {
var widget = _step.value;
widget.update();
}
} catch (err) {
_iterator.e(err);
} finally {
_iterator.f();
}
}
if (_this.onRender) {
_this.onRender([_this.r0, _this.r1, _this.r2, _this.r3]);
}
}
// request new frame
requestAnimationFrame(animate);
}
// renderers
DicomViewerUtils.initRenderer3D(_this.r0, _this.containerRef.current);
DicomViewerUtils.initRenderer2D(_this.r1, _this.containerRef.current);
DicomViewerUtils.initRenderer2D(_this.r2, _this.containerRef.current);
DicomViewerUtils.initRenderer2D(_this.r3, _this.containerRef.current);
_this.resizeObserver = new ResizeObserver(function (entries) {
var _iterator2 = _createForOfIteratorHelper(entries),
_step2;
try {
for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
var entry = _step2.value;
var _entry$contentRect = entry.contentRect,
width = _entry$contentRect.width,
height = _entry$contentRect.height;
_this.r0.renderer.setSize(width, height);
_this.r0.camera.aspect = width / height;
_this.r0.camera.updateProjectionMatrix();
_this.r0.controls.handleResize();
}
} catch (err) {
_iterator2.e(err);
} finally {
_iterator2.f();
}
});
_this.resizeObserver.observe(_this.r0.domElement);
// start rendering loop
animate();
}
// init threeJS
init();
/*
* load sequence for each file
* instantiate the loader
* it loads and parses the dicom image
*/
var loader = new VolumeLoader();
loader.load(this.extractFilesPath(data)).then(function () {
var applySegmentationLUT = _this.props.applySegmentationLUT;
var series = loader.data[0].mergeSeries(loader.data)[0];
loader.free();
loader = null;
// get first stack from series
var stack = series.stack[0];
stack.prepare();
// center 3d camera/control on the stack
var centerLPS = stack.worldCenter();
_this.r0.camera.lookAt(centerLPS.x, centerLPS.y, centerLPS.z);
_this.r0.camera.updateProjectionMatrix();
_this.r0.controls.target.set(centerLPS.x, centerLPS.y, centerLPS.z);
// bounding box
var boxHelper = new HelpersBoundingBox(stack);
_this.r0.scene.add(boxHelper);
// Init the 2D planes stack helpers
DicomViewerUtils.initHelpersStack(_this.r1, stack);
DicomViewerUtils.initHelpersStack(_this.r2, stack);
DicomViewerUtils.initHelpersStack(_this.r3, stack);
// Init the 2D planes segmentation LUT if necessary
if (applySegmentationLUT) {
DicomViewerUtils.initSegmentationLUT(_this.r1, stack, _this.r1.stackHelper);
DicomViewerUtils.initSegmentationLUT(_this.r2, stack, _this.r2.stackHelper);
DicomViewerUtils.initSegmentationLUT(_this.r3, stack, _this.r3.stackHelper);
}
var planesSetup = {
'r1': ['r2', 'r3'],
// red slice
'r2': ['r1', 'r3'],
// yellow slice
'r3': ['r1', 'r2'] // green slice
};
// Initialize 2D planes localizer (cross on 2D planes)
for (var _i2 = 0, _Object$entries2 = Object.entries(planesSetup); _i2 < _Object$entries2.length; _i2++) {
var _Object$entries2$_i = _slicedToArray(_Object$entries2[_i2], 2),
planeName = _Object$entries2$_i[0],
linkedPlaneNames = _Object$entries2$_i[1];
var plane = _this[planeName];
// Add each 2D plane to the 3D scene
_this.r0.scene.add(plane.scene);
/*
* Init the localizer
* create new mesh with Localizer shaders
*/
var planeEquation = plane.stackHelper.slice.cartesianEquation();
/*
* create new mesh with Localizer shaders
* compute the dependent stack helper
*/
var linkedPlanes = linkedPlaneNames.map(function (name) {
var linkedStackHelper = _this[name].stackHelper;
return {
plane: linkedStackHelper.slice.cartesianEquation(),
color: new THREE.Color(linkedStackHelper.borderColor)
};
});
DicomViewerUtils.initHelpersLocalizer(plane, stack, planeEquation, linkedPlanes);
}
_this.configureEvents();
_this.updateLayout(_this.props.mode);
_this.ready = true;
_this.setState({
ready: true
});
_this.props.onLoaded();
})["catch"](function (error) {
window.console.log('oops... something went wrong...');
window.console.log(error);
});
}
}, {
key: "updateLayout",
value: function updateLayout(mode) {
if (mode === 'single_view') {
this.setSingleLayout(this.props.orientation);
} else {
this.setQuadLayout();
}
}
}, {
key: "centerOn",
value: function centerOn(plane, point) {
if (!plane.stackHelper) {
return;
}
var stackHelper = plane.stackHelper;
var ijk = StackModel.worldToData(stackHelper.stack, point);
this.r1.stackHelper.index = ijk.getComponent((this.r1.stackHelper.orientation + 2) % 3);
this.r2.stackHelper.index = ijk.getComponent((this.r2.stackHelper.orientation + 2) % 3);
this.r3.stackHelper.index = ijk.getComponent((this.r3.stackHelper.orientation + 2) % 3);
DicomViewerUtils.updateLocalizer(this.r2, [this.r1.localizerHelper, this.r3.localizerHelper]);
DicomViewerUtils.updateLocalizer(this.r1, [this.r2.localizerHelper, this.r3.localizerHelper]);
DicomViewerUtils.updateLocalizer(this.r3, [this.r1.localizerHelper, this.r2.localizerHelper]);
}
}, {
key: "clickedOnPoint",
value: function clickedOnPoint(event, callback) {
var canvas = event.srcElement.parentElement;
var id = event.target.id;
var mouse = {
x: (event.clientX - offset(canvas).left) / canvas.clientWidth * 2 - 1,
y: -((event.clientY - offset(canvas).top) / canvas.clientHeight) * 2 + 1
};
var plane = this["r".concat(id)];
if (!plane) {
return;
}
var camera = plane.camera;
// const stackHelper = plane.stackHelper;
var scene = plane.scene;
var raycaster = new THREE.Raycaster();
raycaster.setFromCamera(mouse, camera);
var intersects = raycaster.intersectObjects(scene.children, true);
if (intersects.length > 0) {
callback(plane, intersects[0].point, event);
}
}
}, {
key: "configureEvents",
value: function configureEvents() {
var _this3 = this;
var _this = this;
function goToPoint(event) {
_this.clickedOnPoint(event, _this.centerOn);
}
function pointClickedOn(event) {
_this.clickedOnPoint(event, _this.props.pointClickedOn);
}
function goToSingleView(event) {
var id = event.target.id;
var orientation = null;
switch (id) {
case '0':
orientation = '3d';
break;
case '1':
orientation = 'sagittal';
break;
case '2':
orientation = 'axial';
break;
case '3':
orientation = 'coronal';
break;
}
if (orientation != null) {
_this.setState({
mode: 'single_view',
orientation: orientation
});
}
}
/*
* function toggleMode (event) {
* if (_this.props.mode === 'single_view') {
* _this.changeMode();
* } else {
* goToSingleView(event);
* }
* }
*/
function onScroll(event) {
var view = event.target.domElement.dataset.view;
var stackHelper = null;
switch (view) {
case 'r1':
stackHelper = _this.r1.stackHelper;
break;
case 'r2':
stackHelper = _this.r2.stackHelper;
break;
case 'r3':
stackHelper = _this.r3.stackHelper;
break;
}
if (event.delta > 0) {
if (stackHelper.index >= stackHelper.orientationMaxIndex - 1) {
return false;
}
stackHelper.index += 1;
} else {
if (stackHelper.index <= 0) {
return false;
}
stackHelper.index -= 1;
}
DicomViewerUtils.updateLocalizer(_this.r2, [_this.r1.localizerHelper, _this.r3.localizerHelper]);
DicomViewerUtils.updateLocalizer(_this.r1, [_this.r2.localizerHelper, _this.r3.localizerHelper]);
DicomViewerUtils.updateLocalizer(_this.r3, [_this.r1.localizerHelper, _this.r2.localizerHelper]);
return true;
}
function performEventAction(action, event) {
// Check if it is a already defined action or a external one
if (action === 'goToPoint' || action === 'goToSingleView' || action === 'toggleMode' || action === 'pointClickedOn') {
eval(action + '(event)');
} else {
action(event, this);
}
}
function eventHandling(event) {
if (_this.drag) {
return;
}
if (event.type === 'mouseup' && event.which === 3 && _this.props.onRightClick) {
performEventAction(_this.props.onRightClick, event);
}
var isClick = event.type === 'click';
if (isClick && (event.ctrlKey || event.metaKey) && _this.props.onCtrlClick) {
performEventAction(_this.props.onCtrlClick, event);
} else if (isClick && event.shiftKey && _this.props.onShiftClick) {
performEventAction(_this.props.onShiftClick, event);
} else if (isClick && _this.props.onClick) {
performEventAction(_this.props.onClick, event);
} else if (event.type === 'dblclick' && _this.props.onDoubleClick) {
performEventAction(_this.props.onDoubleClick, event);
}
}
// event listeners ondoubleclick
_this.r0.domElement.addEventListener('dblclick', eventHandling);
_this.r1.domElement.addEventListener('dblclick', eventHandling);
_this.r2.domElement.addEventListener('dblclick', eventHandling);
_this.r3.domElement.addEventListener('dblclick', eventHandling);
// event listeners onclick
this.r0.domElement.addEventListener('click', eventHandling);
this.r1.domElement.addEventListener('click', eventHandling);
this.r2.domElement.addEventListener('click', eventHandling);
this.r3.domElement.addEventListener('click', eventHandling);
this.r0.domElement.addEventListener('mouseup', eventHandling);
this.r1.domElement.addEventListener('mouseup', eventHandling);
this.r2.domElement.addEventListener('mouseup', eventHandling);
this.r3.domElement.addEventListener('mouseup', eventHandling);
// event listeners on scrol
this.r1.controls.addEventListener('OnScroll', onScroll);
this.r2.controls.addEventListener('OnScroll', onScroll);
this.r3.controls.addEventListener('OnScroll', onScroll);
// event listener on mouse down/up/move to detect drag on all views
for (var _i3 = 0, _arr = ['r0', 'r1', 'r2', 'r3']; _i3 < _arr.length; _i3++) {
var subref = _arr[_i3];
this[subref].domElement.addEventListener('mousedown', function () {
_this3.drag = false;
return false;
});
this[subref].domElement.addEventListener('mousemove', function () {
_this3.drag = true;
});
this[subref].domElement.addEventListener('mouseout', function () {
_this3.drag = false;
});
}
}
}, {
key: "setQuadLayout",
value: function setQuadLayout() {
// update 3D
DicomViewerUtils.windowResize3D(this.r0);
// update 2d
DicomViewerUtils.windowResize2D(this.r1);
DicomViewerUtils.windowResize2D(this.r2);
DicomViewerUtils.windowResize2D(this.r3);
}
}, {
key: "setSingleLayout",
value: function setSingleLayout(orientation) {
switch (orientation) {
case '3d':
DicomViewerUtils.windowResize3D(this.r0);
break;
case 'sagittal':
DicomViewerUtils.windowResize2D(this.r1);
break;
case 'axial':
DicomViewerUtils.windowResize2D(this.r2);
break;
case 'coronal':
DicomViewerUtils.windowResize2D(this.r3);
break;
}
}
}, {
key: "componentWillUnmount",
value: function componentWillUnmount() {
if (this.resizeObserver && this.r0.domElement) {
this.resizeObserver.unobserve(this.r0.domElement);
this.resizeObserver.disconnect();
}
DicomViewerUtils.dispose(this.r0);
DicomViewerUtils.dispose(this.r1);
DicomViewerUtils.dispose(this.r2);
DicomViewerUtils.dispose(this.r3);
}
}, {
key: "componentDidMount",
value: function componentDidMount() {
this.loadModel(this.props.data);
}
}, {
key: "componentDidUpdate",
value: function componentDidUpdate(prevProps, prevState) {
if (this.props.mode !== prevProps.mode || this.props.orientation !== prevProps.orientation || this.props.update > 1 && this.props.update !== prevProps.update || prevState.ready !== this.state.ready && !prevState.ready) {
try {
this.updateLayout(this.props.mode);
} catch (e) {
// not ready yet
}
}
}
}, {
key: "shouldComponentUpdate",
value: function shouldComponentUpdate(nextProps, nextState) {
this.animationOn = true;
return nextProps.data !== this.props.data || nextProps.update !== this.props.update || nextProps.fullScreen !== this.props.fullScreen || nextProps.mode !== this.props.mode || nextProps.orientation !== this.props.orientation || nextState.ready !== this.state.ready && nextState.ready;
}
}, {
key: "stopAnimation",
value: function stopAnimation() {
this.animationOn = false;
}
}, {
key: "startAnimation",
value: function startAnimation() {
this.animationOn = true;
}
}, {
key: "download",
value: function download() {
createZipFromRemoteFiles(this.props.data, !Array.isArray(this.props.data) ? this.props.data : "data.zip");
}
}, {
key: "getCustomButtons",
value: function getCustomButtons() {
var customButtons = [];
var toolbarButtons = this.props.toolbarButtons;
var addButtons = function addButtons(buttons) {
if (!buttons) {
return;
}
customButtons.push.apply(customButtons, _toConsumableArray(buttons.map(function (b) {
return _objectSpread(_objectSpread({}, b), {}, {
id: b.tooltip
});
})));
};
if (this.props.mode == 'single_view') {
addButtons(toolbarButtons === null || toolbarButtons === void 0 ? void 0 : toolbarButtons.single_view);
} else {
addButtons(toolbarButtons === null || toolbarButtons === void 0 ? void 0 : toolbarButtons.quad_view);
}
if (this.props.showDownloadButton) {
customButtons.push({
icon: faDownload,
id: 'Download',
tooltip: 'Download',
action: this.download
});
}
if (this.props.fullScreen) {
addButtons(toolbarButtons === null || toolbarButtons === void 0 ? void 0 : toolbarButtons.fullScreen);
} else {
addButtons(toolbarButtons === null || toolbarButtons === void 0 ? void 0 : toolbarButtons.minimized);
}
return customButtons;
}
}, {
key: "hasCustomButtons",
value: function hasCustomButtons() {
var buttons = this.props.toolbarButtons;
return (buttons === null || buttons === void 0 ? void 0 : buttons.single_view) || (buttons === null || buttons === void 0 ? void 0 : buttons.quad_view) || (buttons === null || buttons === void 0 ? void 0 : buttons.fullScreen) || (buttons === null || buttons === void 0 ? void 0 : buttons.minimized) || this.props.showDownloadButton;
}
}, {
key: "render",
value: function render() {
var _this$props2 = this.props,
toolbarOptions = _this$props2.toolbarOptions,
loaderOptions = _this$props2.loaderOptions,
mode = _this$props2.mode,
orientation = _this$props2.orientation,
id = _this$props2.id,
fullScreen = _this$props2.fullScreen;
var customButtons = this.getCustomButtons();
var containerStyle = fullScreen ? {
position: 'fixed',
top: '0',
left: '0',
zIndex: '9999',
background: '#121212',
height: '100%',
width: '100%'
} : {
height: '100%',
width: '100%'
};
var displayView = function displayView(o) {
return mode === 'single_view' && orientation === o;
};
var display3DView = displayView('3d');
var doNotDisplay = function doNotDisplay(o) {
return mode === 'single_view' && orientation !== o;
};
var showLoader = loaderOptions && loaderOptions.showLoader;
var loader = loaderOptions && loaderOptions.instance ? /*#__PURE__*/React.createElement(loaderOptions.instance, loaderOptions.props) : /*#__PURE__*/React.createElement(Loader, {
fullscreen: fullScreen,
handleClose: toolbarOptions === null || toolbarOptions === void 0 ? void 0 : toolbarOptions.handleClose,
messages: toolbarOptions === null || toolbarOptions === void 0 ? void 0 : toolbarOptions.messages,
messagesInterval: toolbarOptions === null || toolbarOptions === void 0 ? void 0 : toolbarOptions.messagesInterval,
elapsed: toolbarOptions === null || toolbarOptions === void 0 ? void 0 : toolbarOptions.elapsed,
backgroundStyle: toolbarOptions === null || toolbarOptions === void 0 ? void 0 : toolbarOptions.backgroundStyle
});
var toolbar = toolbarOptions && toolbarOptions.instance ? /*#__PURE__*/React.createElement(toolbarOptions.instance, _extends({
buttons: customButtons
}, toolbarOptions.props)) : /*#__PURE__*/React.createElement(CustomToolbar, {
buttons: customButtons,
containerStyles: toolbarOptions === null || toolbarOptions === void 0 ? void 0 : toolbarOptions.containerStyles,
toolBarClassName: toolbarOptions === null || toolbarOptions === void 0 ? void 0 : toolbarOptions.toolBarClassName,
innerDivStyles: toolbarOptions === null || toolbarOptions === void 0 ? void 0 : toolbarOptions.innerDivStyles,
buttonStyles: toolbarOptions === null || toolbarOptions === void 0 ? void 0 : toolbarOptions.buttonStyles
});
return /*#__PURE__*/React.createElement("div", {
ref: this.containerRef,
key: "".concat(id, "_component"),
id: "".concat(id, "_component"),
style: containerStyle,
onMouseLeave: this.stopAnimation,
className: "container-dicom-viewer"
}, !this.state.ready && showLoader && loader, this.hasCustomButtons() && toolbar, /*#__PURE__*/React.createElement("div", {
className: classes.dicomViewer,
style: {
height: '90%',
width: '100%',
display: 'flex',
flexWrap: 'wrap',
backgroundColor: '#353535'
}
}, /*#__PURE__*/React.createElement("div", {
id: "r0",
className: classes.renderer + ' r0',
"data-view": "r0",
style: {
display: doNotDisplay('3d') ? 'none' : '',
width: display3DView ? '100%' : '50%',
height: display3DView ? '100%' : '50%',
backgroundColor: '#000',
"float": 'left'
},
onMouseEnter: this.startAnimation
}), /*#__PURE__*/React.createElement("div", {
id: "r1",
className: classes.renderer + ' r1',
"data-view": "r1",
style: {
display: doNotDisplay('sagittal') ? 'none' : '',
width: displayView('sagittal') ? '100%' : '50%',
height: displayView('sagittal') ? '100%' : '50%',
backgroundColor: '#000',
"float": 'left'
},
onMouseEnter: this.startAnimation
}), /*#__PURE__*/React.createElement("div", {
id: "r2",
className: classes.renderer + ' r2',
"data-view": "r2",
style: {
display: doNotDisplay('axial') ? 'none' : '',
width: displayView('axial') ? '100%' : '50%',
height: displayView('axial') ? '100%' : '50%',
backgroundColor: '#000',
"float": 'left'
},
onMouseEnter: this.startAnimation
}), /*#__PURE__*/React.createElement("div", {
id: "r3",
className: classes.renderer + ' r3',
"data-view": "r3",
style: {
display: doNotDisplay('coronal') ? 'none' : '',
width: displayView('coronal') ? '100%' : '50%',
height: displayView('coronal') ? '100%' : '50%',
backgroundColor: '#000',
"float": 'left'
},
onMouseEnter: this.startAnimation
}), /*#__PURE__*/React.createElement("div", {
className: "segmentationLUT-r1",
style: {
visibility: "hidden"
}
}), /*#__PURE__*/React.createElement("div", {
className: "segmentationLUT-r2",
style: {
visibility: "hidden"
}
}), /*#__PURE__*/React.createElement("div", {
className: "segmentationLUT-r3",
style: {
visibility: "hidden"
}
})));
}
}]);
}(Component);
var DicomViewer = function DicomViewer(props) {
return /*#__PURE__*/React.createElement(_DicomViewer, props);
};
DicomViewer.defaultProps = {
onLoaded: function onLoaded() {},
mode: 'quad_view',
orientation: '3d',
onClick: 'goToPoint',
onCtrlClick: 'goToPoint',
onShiftClick: 'goToPoint',
onDoubleClick: 'goToPoint',
onRightClick: undefined,
showDownloadButton: false,
toolbarOptions: null,
loaderOptions: {
showLoader: true
}
};
DicomViewer.propTypes = {
/**
* Component identifier
*/
id: PropTypes.string.isRequired,
/**
* Path/URL to file (f.e. "/path/to/my/file.gz")
*/
data: PropTypes.string.isRequired,
/**
* View mode: 'single_view' or 'quad_view'
*/
mode: PropTypes.oneOf(['single_view', 'quad_view']),
/**
* Display the dicom viewer in full screen
*/
fullScreen: PropTypes.bool,
/**
* Orientation view: '3d', 'coronal', 'axial' or 'sagittal'
*/
orientation: PropTypes.oneOf(['3d', 'coronal', 'axial', 'sagittal']),
/**
* Action to perform on click: 'goToPoint', or custom action
*/
onClick: PropTypes.oneOfType([PropTypes.oneOf(['goToPoint']), PropTypes.func]),
/**
* Action to performe on Ctrl click: 'goToPoint' or custom action
*/
onCtrlClick: PropTypes.oneOfType([PropTypes.oneOf(['goToPoint']), PropTypes.func]),
/**
* Action to performe on Shift click: 'goToPoint' or custom action
*/
onShiftClick: PropTypes.oneOfType([PropTypes.oneOf(['goToPoint']), PropTypes.func]),
/**
* Action to performe on right click: 'goToPoint' or custom action
*/
onRightClick: PropTypes.oneOfType([PropTypes.oneOf(['goToPoint']), PropTypes.func]),
/**
* Action to performe on double click: 'goToPoint' or custom action
*/
onDoubleClick: PropTypes.oneOfType([PropTypes.oneOf(['goToPoint']), PropTypes.func]),
/**
* Enables segmentation LUT
*/
applySegmentationLUT: PropTypes.bool,
/**
* Bool that defines the showing or not of the download button
*/
showDownloadButton: PropTypes.bool,
/**
* Callback function to be called after load is complete
*/
onLoaded: PropTypes.func,
/**
* Options to customize the toolbar
*/
toolbarOptions: PropTypes.shape({
/**
* Reference to toolbar component
*/
instance: PropTypes.elementType,
/**
* Custom toolbar props
*/
props: PropTypes.shape({}),
/**
* Styles to be applied to the toolbar container
*/
containerStyles: PropTypes.shape({}),
/**
* Styles to be applied to the toolbar
*/
toolBarClassName: PropTypes.shape({}),
/**
* Styles to be applied to the inner div
*/
innerDivStyles: PropTypes.shape({}),
/**
* Styles to be applied to the buttons
*/
buttonStyles: PropTypes.shape({})
}),
/**
* Options to customize the loader
*/
loaderOptions: PropTypes.shape({
/**
* Reference to toolbar component
*/
instance: PropTypes.elementType,
/**
* Custom loader props
*/
props: PropTypes.shape({}),
/**
* Bool to control the use of the loader
*/
showLoader: PropTypes.bool,
/**
* Function to handle the close of the Loader
*/
handleClose: PropTypes.func,
/**
* Array of Custom messages to display
*/
messages: PropTypes.array,
/**
* Number of milliseconds between custom messages
*/
messagesInterval: PropTypes.number,
/**
* Number of the progress value to show in linear determinate (in percentage)
*/
elapsed: PropTypes.number,
/**
* Style to be applied to the Loader background
*/
backgroundStyle: PropTypes.shape({
/**
* Loader's background color. Defaults to rgba(255,142,0,0.1)
*/
backgroundColor: PropTypes.string
})
}),
/**
* Buttons and action to add depending in the view mode/full screen
*/
toolbarButtons: PropTypes.shape({
/**
* Buttons to display if the view is minimized
*/
minimized: PropTypes.arrayOf(PropTypes.shape({
/**
* The button icon
*/
icon: PropTypes.string,
/**
* The tooltip of the button
*/
tooltip: PropTypes.string,
/**
* A callback that will be called when the button is clicked
*/
action: PropTypes.func
})),
/**
* Buttons to display if the view is full screen
*/
fullScreen: PropTypes.arrayOf(PropTypes.shape({
/**
* The button icon
*/
icon: PropTypes.string,
/**
* The tooltip of the button
*/
tooltip: PropTypes.string,
/**
* A callback that will be called when the button is clicked
*/
action: PropTypes.func
})),
/**
* Buttons to display if the view is in single view mode
*/
single_view: PropTypes.arrayOf(PropTypes.shape({
/**
* The button icon
*/
icon: PropTypes.string,
/**
* The tooltip of the button
*/
tooltip: PropTypes.string,
/**
* A callback that will be called when the button is clicked
*/
action: PropTypes.func
})),
/**
* Buttons to display if the view in quad view mode
*/
quad_view: PropTypes.arrayOf(PropTypes.shape({
/**
* The button icon
*/
icon: PropTypes.string,
/**
* The tooltip of the button
*/
tooltip: PropTypes.string,
/**
* A callback that will be called when the button is clicked
*/
action: PropTypes.func
}))
})
};
export default DicomViewer;