add new viewer folder
This commit is contained in:
parent
d541c23117
commit
fc5bd73c1e
2164
OHIFViewer-react/README.md
Normal file
2164
OHIFViewer-react/README.md
Normal file
File diff suppressed because it is too large
Load Diff
40
OHIFViewer-react/package.json
Normal file
40
OHIFViewer-react/package.json
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
{
|
||||||
|
"name": "react-cornerstone-viewport-example",
|
||||||
|
"version": "0.0.0",
|
||||||
|
"license": "MIT",
|
||||||
|
"private": true,
|
||||||
|
"dependencies": {
|
||||||
|
"cornerstone-core": "2.2.8",
|
||||||
|
"cornerstone-math": "0.1.7",
|
||||||
|
"cornerstone-tools": "3.0.0-b.1471",
|
||||||
|
"cornerstone-wado-image-loader": "2.2.3",
|
||||||
|
"dicom-parser": "1.8.3",
|
||||||
|
"hammerjs": "^2.0.8",
|
||||||
|
"lodash.debounce": "^4.0.8",
|
||||||
|
"moment": "^2.22.2",
|
||||||
|
"prop-types": "^15.6.2",
|
||||||
|
"react": "^16.4.1",
|
||||||
|
"react-cornerstone-viewport": "^0.1.1",
|
||||||
|
"react-dom": "^16.4.1",
|
||||||
|
"react-scripts": "^2.1.1",
|
||||||
|
"react-viewerbase": "^0.1.2",
|
||||||
|
"ohif-core": "link:/Users/Erik/Projects/OHIF/ohif-core"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"copy:webworkers": "cp node_modules/cornerstone-wado-image-loader/dist/*.min.js* public",
|
||||||
|
"version": "node -p -e \"'export default \\'' + require('./package.json').version + '\\';'\" > src/version.js",
|
||||||
|
"sha": "echo \"export default '\"$(git rev-parse HEAD)\"';\" > src/sha.js",
|
||||||
|
"details": "npm run version && npm run sha",
|
||||||
|
"prep": "npm run details && npm run copy:webworkers",
|
||||||
|
"start": "npm run prep && react-scripts start",
|
||||||
|
"build": "npm run prep && react-scripts build",
|
||||||
|
"test": "react-scripts test --env=jsdom",
|
||||||
|
"eject": "react-scripts eject"
|
||||||
|
},
|
||||||
|
"browserslist": [
|
||||||
|
">0.2%",
|
||||||
|
"not dead",
|
||||||
|
"not ie <= 11",
|
||||||
|
"not op_mini all"
|
||||||
|
]
|
||||||
|
}
|
||||||
3
OHIFViewer-react/public/cornerstoneWADOImageLoader.min.js
vendored
Normal file
3
OHIFViewer-react/public/cornerstoneWADOImageLoader.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
4
OHIFViewer-react/public/cornerstoneWADOImageLoaderCodecs.min.js
vendored
Normal file
4
OHIFViewer-react/public/cornerstoneWADOImageLoaderCodecs.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
3
OHIFViewer-react/public/cornerstoneWADOImageLoaderWebWorker.min.js
vendored
Normal file
3
OHIFViewer-react/public/cornerstoneWADOImageLoaderWebWorker.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
23
OHIFViewer-react/public/index.html
Normal file
23
OHIFViewer-react/public/index.html
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||||
|
<meta name="theme-color" content="#000000">
|
||||||
|
|
||||||
|
<link rel="manifest" href="%PUBLIC_URL%/manifest.json">
|
||||||
|
|
||||||
|
<title>react-cornerstone-viewport</title>
|
||||||
|
|
||||||
|
<!-- Latest compiled and minified CSS -->
|
||||||
|
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<noscript>
|
||||||
|
You need to enable JavaScript to run this app.
|
||||||
|
</noscript>
|
||||||
|
|
||||||
|
<div id="root"></div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
8
OHIFViewer-react/public/manifest.json
Normal file
8
OHIFViewer-react/public/manifest.json
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"short_name": "react-cornerstone-viewport",
|
||||||
|
"name": "react-cornerstone-viewport",
|
||||||
|
"start_url": "./index.html",
|
||||||
|
"display": "standalone",
|
||||||
|
"theme_color": "#000000",
|
||||||
|
"background_color": "#ffffff"
|
||||||
|
}
|
||||||
16
OHIFViewer-react/src/App.css
Normal file
16
OHIFViewer-react/src/App.css
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
html,
|
||||||
|
body,
|
||||||
|
#root {
|
||||||
|
background-color: black;
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
box-sizing: border-box;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
}
|
||||||
83
OHIFViewer-react/src/App.js
Normal file
83
OHIFViewer-react/src/App.js
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
import React, { Component } from 'react';
|
||||||
|
import { withRouter } from 'react-router';
|
||||||
|
import { Route, Switch } from 'react-router-dom';
|
||||||
|
import { OHIF } from 'ohif-core';
|
||||||
|
import ViewerRouting from "./ViewerRouting.js";
|
||||||
|
import IHEInvokeImageDisplay from './IHEInvokeImageDisplay.js';
|
||||||
|
import './App.css';
|
||||||
|
|
||||||
|
const reload = () => window.location.reload();
|
||||||
|
const Studylist = OHIF.studylist.components.StudyList;
|
||||||
|
|
||||||
|
function setContext(context) {
|
||||||
|
/*Rollbar.configure({
|
||||||
|
payload: {
|
||||||
|
context
|
||||||
|
}
|
||||||
|
});*/
|
||||||
|
console.log(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
class App extends Component {
|
||||||
|
componentDidMount() {
|
||||||
|
// Temporary until the rest of the UI is in React
|
||||||
|
window.router = {
|
||||||
|
history: this.props.history
|
||||||
|
};
|
||||||
|
|
||||||
|
this.unlisten = this.props.history.listen((location, action) => {
|
||||||
|
setContext(window.location.pathname);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
this.unlisten();
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<Switch>
|
||||||
|
<Route
|
||||||
|
exact
|
||||||
|
path="/studylist"
|
||||||
|
component={Studylist}
|
||||||
|
/*auth={this.props.auth}*/
|
||||||
|
store={this.props.store}
|
||||||
|
/>
|
||||||
|
<Route
|
||||||
|
exact
|
||||||
|
path="/"
|
||||||
|
component={Studylist}
|
||||||
|
/*auth={this.props.auth}*/
|
||||||
|
store={this.props.store}
|
||||||
|
/>
|
||||||
|
<Route
|
||||||
|
path="/viewer/:studyInstanceUids"
|
||||||
|
component={ViewerRouting}
|
||||||
|
/*auth={this.props.auth}*/
|
||||||
|
store={this.props.store}
|
||||||
|
/>
|
||||||
|
<Route
|
||||||
|
path="/study/:studyInstanceUid/series/:seriesInstanceUids"
|
||||||
|
component={ViewerRouting}
|
||||||
|
/*auth={this.props.auth}*/
|
||||||
|
store={this.props.store}
|
||||||
|
/>
|
||||||
|
<Route
|
||||||
|
path="/IHEInvokeImageDisplay"
|
||||||
|
component={IHEInvokeImageDisplay}
|
||||||
|
/*auth={this.props.auth}*/
|
||||||
|
store={this.props.store}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/*<Route path="/silent-refresh.html" onEnter={reload} />
|
||||||
|
<Route path="/logout-redirect.html" onEnter={reload} />*/}
|
||||||
|
<Route render={() =>
|
||||||
|
<div> Sorry, this page does not exist. </div>}
|
||||||
|
/>
|
||||||
|
</Switch>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default withRouter(App);
|
||||||
33
OHIFViewer-react/src/IHEInvokeImageDisplay.js
Normal file
33
OHIFViewer-react/src/IHEInvokeImageDisplay.js
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
import React, { Component } from "react";
|
||||||
|
import PropTypes from "prop-types";
|
||||||
|
import ViewerFromStudyData from "./ViewerFromStudyData.js";
|
||||||
|
|
||||||
|
function IHEInvokeImageDisplay({ match }) {
|
||||||
|
const requestType = match.params.query.requestType;
|
||||||
|
|
||||||
|
let studyInstanceUids;
|
||||||
|
let displayStudyList = false;
|
||||||
|
if (requestType === "STUDY") {
|
||||||
|
studyInstanceUids = match.params.query.studyUID.split(';');
|
||||||
|
} else if (requestType === "STUDYBASE64") {
|
||||||
|
const uids = this.params.query.studyUID;
|
||||||
|
const decodedData = window.atob(uids);
|
||||||
|
studyInstanceUids = decodedData.split(';');
|
||||||
|
} else if (requestType === "PATIENT") {
|
||||||
|
const patientUids = this.params.query.patientID.split(';');
|
||||||
|
displayStudyList = true
|
||||||
|
} else {
|
||||||
|
displayStudyList = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if (displayStudyList) {
|
||||||
|
//return (<StudyList/>);
|
||||||
|
return ('');
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ViewerFromStudyData studyInstanceUids={studyInstanceUids}/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default IHEInvokeImageDisplay;
|
||||||
102
OHIFViewer-react/src/ViewerFromStudyData.js
Normal file
102
OHIFViewer-react/src/ViewerFromStudyData.js
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
import React, {Component} from "react";
|
||||||
|
import PropTypes from "prop-types";
|
||||||
|
import Viewer from "./viewer/viewer.js";
|
||||||
|
import { OHIF } from 'ohif-core';
|
||||||
|
|
||||||
|
// TODO: Move to react-viewerbase
|
||||||
|
|
||||||
|
function createDisplaySets(studies) {
|
||||||
|
// Define the OHIF.viewer.data global object
|
||||||
|
// TODO: Save all data that is currently in OHIF.viewer in redux instead
|
||||||
|
OHIF.viewer.data = OHIF.viewer.data || {};
|
||||||
|
|
||||||
|
// @TypeSafeStudies
|
||||||
|
// Clears OHIF.viewer.Studies collection
|
||||||
|
OHIF.viewer.Studies.removeAll();
|
||||||
|
|
||||||
|
// @TypeSafeStudies
|
||||||
|
// Clears OHIF.viewer.StudyMetadataList collection
|
||||||
|
OHIF.viewer.StudyMetadataList.removeAll();
|
||||||
|
|
||||||
|
OHIF.viewer.data.studyInstanceUids = [];
|
||||||
|
|
||||||
|
const updatedStudies = studies.map(study => {
|
||||||
|
const studyMetadata = new OHIF.metadata.OHIFStudyMetadata(study, study.studyInstanceUid);
|
||||||
|
let displaySets = study.displaySets;
|
||||||
|
|
||||||
|
if (!study.displaySets) {
|
||||||
|
displaySets = OHIF.viewerbase.sortingManager.getDisplaySets(studyMetadata);
|
||||||
|
study.displaySets = displaySets;
|
||||||
|
}
|
||||||
|
|
||||||
|
studyMetadata.setDisplaySets(displaySets);
|
||||||
|
|
||||||
|
study.selected = true;
|
||||||
|
OHIF.viewer.Studies.insert(study);
|
||||||
|
OHIF.viewer.StudyMetadataList.insert(studyMetadata);
|
||||||
|
OHIF.viewer.data.studyInstanceUids.push(study.studyInstanceUid);
|
||||||
|
|
||||||
|
// Updates WADO-RS metaDataManager
|
||||||
|
OHIF.viewerbase.updateMetaDataManager(study);
|
||||||
|
|
||||||
|
return study;
|
||||||
|
});
|
||||||
|
|
||||||
|
return updatedStudies;
|
||||||
|
}
|
||||||
|
|
||||||
|
class ViewerFromStudyData extends Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
studies: null,
|
||||||
|
error: null
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
// TODO: Avoid using timepoints here
|
||||||
|
//const params = { studyInstanceUids, seriesInstanceUids, timepointId, timepointsFilter={} };
|
||||||
|
const params = {
|
||||||
|
studyInstanceUids: this.props.studyInstanceUids,
|
||||||
|
seriesInstanceUids: this.props.seriesInstanceUids,
|
||||||
|
}
|
||||||
|
const promise = OHIF.viewerbase.prepareViewerData(params);
|
||||||
|
|
||||||
|
// Render the viewer when the data is ready
|
||||||
|
promise.then(({ studies, viewerData }) => {
|
||||||
|
OHIF.viewer.data = viewerData;
|
||||||
|
|
||||||
|
const updatedStudies = createDisplaySets(studies);
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
studies: updatedStudies,
|
||||||
|
});
|
||||||
|
}).catch(error => {
|
||||||
|
this.setState({
|
||||||
|
error,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
if (this.state.error) {
|
||||||
|
return (<div>Error: {JSON.stringify(this.state.error)}</div>);
|
||||||
|
} else if (!this.state.studies) {
|
||||||
|
return (<div>Loading...</div>);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Viewer studies={this.state.studies}/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ViewerFromStudyData.propTypes = {
|
||||||
|
studyInstanceUids: PropTypes.array.isRequired,
|
||||||
|
seriesInstanceUids: PropTypes.array
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ViewerFromStudyData;
|
||||||
34
OHIFViewer-react/src/ViewerRouting.js
Normal file
34
OHIFViewer-react/src/ViewerRouting.js
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
import React, {Component} from "react";
|
||||||
|
import PropTypes from "prop-types";
|
||||||
|
import ViewerFromStudyData from "./ViewerFromStudyData.js";
|
||||||
|
|
||||||
|
// TODO: Move to react-viewerbase
|
||||||
|
|
||||||
|
function ViewerRouting({ match }) {
|
||||||
|
const { studyInstanceUids, seriesInstanceUids } = match.params;
|
||||||
|
|
||||||
|
let studyUIDs;
|
||||||
|
let seriesUIDs;
|
||||||
|
|
||||||
|
if (studyInstanceUids && !seriesInstanceUids) {
|
||||||
|
studyUIDs = studyInstanceUids.split(';');
|
||||||
|
} else if (studyInstanceUids && seriesInstanceUids) {
|
||||||
|
studyUIDs = [match.params.studyInstanceUid];
|
||||||
|
seriesUIDs = match.params.seriesInstanceUids.split(';');
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ViewerFromStudyData studyInstanceUids={studyUIDs} seriesInstanceUids={seriesUIDs}/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
ViewerRouting.propTypes = {
|
||||||
|
match: PropTypes.shape({
|
||||||
|
params: PropTypes.shape({
|
||||||
|
studyInstanceUids: PropTypes.string.isRequired,
|
||||||
|
seriesInstanceUids: PropTypes.string
|
||||||
|
})
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ViewerRouting;
|
||||||
37
OHIFViewer-react/src/config.js
Normal file
37
OHIFViewer-react/src/config.js
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
import { OHIF } from 'ohif-core';
|
||||||
|
import cornerstoneWADOImageLoader from 'cornerstone-wado-image-loader';
|
||||||
|
import sha from './sha.js';
|
||||||
|
import version from './version.js';
|
||||||
|
|
||||||
|
OHIF.info = {
|
||||||
|
sha,
|
||||||
|
version
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
const maxWebWorkers = Math.max(navigator.hardwareConcurrency - 1, 1);
|
||||||
|
const config = {
|
||||||
|
maxWebWorkers,
|
||||||
|
startWebWorkersOnDemand: true,
|
||||||
|
webWorkerPath: OHIF.utils.absoluteUrl('packages/ohif_cornerstone/public/js/cornerstoneWADOImageLoaderWebWorker.es5.js'),
|
||||||
|
taskConfiguration: {
|
||||||
|
decodeTask: {
|
||||||
|
loadCodecsOnStartup: true,
|
||||||
|
initializeCodecsOnStartup: false,
|
||||||
|
codecsPath: OHIF.utils.absoluteUrl('packages/ohif_cornerstone/public/js/cornerstoneWADOImageLoaderCodecs.es5.js'),
|
||||||
|
usePDFJS: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
cornerstoneWADOImageLoader.webWorkerManager.initialize(config);
|
||||||
|
|
||||||
|
cornerstoneWADOImageLoader.configure({
|
||||||
|
beforeSend: function(xhr) {
|
||||||
|
const headers = OHIF.DICOMWeb.getAuthorizationHeader();
|
||||||
|
|
||||||
|
if (headers.Authorization) {
|
||||||
|
xhr.setRequestHeader("Authorization", headers.Authorization);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
169
OHIFViewer-react/src/flexboxLayout/flexboxLayout.js
Normal file
169
OHIFViewer-react/src/flexboxLayout/flexboxLayout.js
Normal file
@ -0,0 +1,169 @@
|
|||||||
|
import React, { Component } from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { OHIF } from 'ohif-core';
|
||||||
|
import { StudyBrowser } from 'react-viewerbase';
|
||||||
|
import cornerstone from 'cornerstone-core';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Asynchronous wrapper around Cornerstone's renderToCanvas method.
|
||||||
|
*
|
||||||
|
* @param {HTMLElement} canvasElement An HTML <canvas> element
|
||||||
|
* @param {Image} image A Cornerstone Image
|
||||||
|
*
|
||||||
|
* @return {Promise} A promise tracking the progress of the rendering. Resolves empty.
|
||||||
|
*/
|
||||||
|
function renderAsync(canvasElement, image) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
try {
|
||||||
|
cornerstone.renderToCanvas(canvasElement, image);
|
||||||
|
resolve();
|
||||||
|
} catch(error) {
|
||||||
|
reject(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Define a handler for success on image load
|
||||||
|
const loadSuccess = function(image, studyInstanceUid, displaySetInstanceUid) {
|
||||||
|
// This is an off-screen canvas. It's used to get dataURL images by using
|
||||||
|
// cornerstone.renderToCanvas function.
|
||||||
|
const canvasElement = document.createElement('canvas');
|
||||||
|
canvasElement.width = 193;
|
||||||
|
canvasElement.height = 123;
|
||||||
|
|
||||||
|
// Render the image to canvas to be able to get its dataURL
|
||||||
|
renderAsync(canvasElement, image).then(() => {
|
||||||
|
const studies = this.state.studiesForBrowser;
|
||||||
|
const study = studies.find(study => study.studyInstanceUid === studyInstanceUid);
|
||||||
|
const thumbnail = study.thumbnails.find(t => t.displaySetInstanceUid === displaySetInstanceUid);
|
||||||
|
|
||||||
|
thumbnail.imageSrc = canvasElement.toDataURL('image/jpeg', 1);
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
studiesForBrowser: studies
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// Define a handler for error on image load
|
||||||
|
const loadError = function(error, studyInstanceUid, displaySetInstanceUid) {
|
||||||
|
const studies = this.state.studiesForBrowser;
|
||||||
|
const study = studies.find(study => study.studyInstanceUid === studyInstanceUid);
|
||||||
|
const thumbnail = study.thumbnails.find(t => t.displaySetInstanceUid === displaySetInstanceUid);
|
||||||
|
|
||||||
|
thumbnail.error = error;
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
studiesForBrowser: studies
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
class FlexboxLayout extends Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
leftSidebarOpen: true,
|
||||||
|
rightSidebarOpen: false,
|
||||||
|
studiesForBrowser: this.getStudiesForBrowser(),
|
||||||
|
};
|
||||||
|
|
||||||
|
this.getStudiesForBrowser = this.getStudiesForBrowser.bind(this);
|
||||||
|
this.getThumbnailsFromImageIds = this.getThumbnailsFromImageIds.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
this.getThumbnailsFromImageIds();
|
||||||
|
}
|
||||||
|
|
||||||
|
getThumbnailsFromImageIds() {
|
||||||
|
const studyThumbnails = this.state.studiesForBrowser.forEach(function (study) {
|
||||||
|
const { studyInstanceUid } = study;
|
||||||
|
|
||||||
|
study.thumbnails.forEach(function (displaySet) {
|
||||||
|
const imageId = displaySet.imageId;
|
||||||
|
const {displaySetInstanceUid, seriesDescription, seriesNumber, instanceNumber, numImageFrames} = displaySet;
|
||||||
|
|
||||||
|
cornerstone.loadAndCacheImage(imageId).then((image) => {
|
||||||
|
loadSuccess.call(this, image, studyInstanceUid, displaySetInstanceUid);
|
||||||
|
}, error => {
|
||||||
|
loadError.call(this, error, studyInstanceUid, displaySetInstanceUid);
|
||||||
|
});
|
||||||
|
}, this);
|
||||||
|
}, this);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
getStudiesForBrowser() {
|
||||||
|
// @TypeSafeStudies
|
||||||
|
const studies = OHIF.viewer.Studies.findAllBy({
|
||||||
|
selected: true
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
// TODO[react]:
|
||||||
|
// - Add sorting of display sets
|
||||||
|
// - Pass in which display set is active from Redux
|
||||||
|
// - Pass in errors and stack loading progress from Redux
|
||||||
|
// - Add useMiddleSeriesInstanceAsThumbnail
|
||||||
|
// - Add showStackLoadingProgressBar option
|
||||||
|
return studies.map((study) => {
|
||||||
|
const { studyInstanceUid } = study;
|
||||||
|
|
||||||
|
const thumbnails = study.displaySets.map((displaySet) => {
|
||||||
|
const active = false;
|
||||||
|
const stackPercentComplete = 0;
|
||||||
|
const { displaySetInstanceUid, seriesDescription, seriesNumber, instanceNumber, numImageFrames } = displaySet;
|
||||||
|
const imageId = displaySet.images[0].getImageId();
|
||||||
|
return {
|
||||||
|
imageSrc: '',
|
||||||
|
imageId,
|
||||||
|
displaySetInstanceUid,
|
||||||
|
seriesDescription,
|
||||||
|
seriesNumber,
|
||||||
|
instanceNumber,
|
||||||
|
numImageFrames,
|
||||||
|
active,
|
||||||
|
stackPercentComplete
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
studyInstanceUid,
|
||||||
|
thumbnails
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
let mainContentClassName = "mainContent"
|
||||||
|
if (this.state.leftSidebarOpen) {
|
||||||
|
mainContentClassName += ' sidebar-left-open';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.state.rightSidebarOpen) {
|
||||||
|
mainContentClassName += ' sidebar-right-open';
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO[react]: Add measurementLightTable
|
||||||
|
return (
|
||||||
|
<div className="viewerSection">
|
||||||
|
<div className={this.state.leftSidebarOpen ? "sidebarMenu sidebar-left sidebar-open" : "sidebarMenu sidebar-left"}>
|
||||||
|
<StudyBrowser studies={this.state.studiesForBrowser}/>
|
||||||
|
</div>
|
||||||
|
<div className={mainContentClassName}>
|
||||||
|
{/* TODO: Needs to be moved from Meteor package to react-viewerbase: <ViewerMain studies={this.props.studies}/>*/}
|
||||||
|
</div>
|
||||||
|
<div className={this.state.rightSidebarOpen ? "sidebarMenu sidebar-right sidebar-open" : "sidebarMenu sidebar-right"}>
|
||||||
|
{/*{{> measurementLightTable (clone this)}}*/}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FlexboxLayout.propTypes = {
|
||||||
|
studies: PropTypes.array.isRequired
|
||||||
|
};
|
||||||
|
|
||||||
|
export default FlexboxLayout;
|
||||||
65
OHIFViewer-react/src/flexboxLayout/flexboxLayout.styl
Normal file
65
OHIFViewer-react/src/flexboxLayout/flexboxLayout.styl
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
.viewerSection
|
||||||
|
display: flex
|
||||||
|
flex: 1
|
||||||
|
flex-flow: row nowrap
|
||||||
|
align-items: stretch
|
||||||
|
height: 'calc(100% - %s)' % ($toolbarHeight)
|
||||||
|
width: 100%
|
||||||
|
|
||||||
|
.sidebarMenu
|
||||||
|
height: 100%
|
||||||
|
// required transformation to make inner fixed elements relative to this one
|
||||||
|
transform(scale(1))
|
||||||
|
transition($sidebarTransition)
|
||||||
|
|
||||||
|
.sidebar-option
|
||||||
|
height: 100%
|
||||||
|
max-width: inherit
|
||||||
|
position: absolute
|
||||||
|
transform(translateX(100%))
|
||||||
|
transition($sidebarTransition)
|
||||||
|
width: 100%
|
||||||
|
|
||||||
|
&.active
|
||||||
|
transform(translateX(0%))
|
||||||
|
|
||||||
|
.sidebar-left
|
||||||
|
theme('border-right', '%s solid $uiBorderColor' % $uiBorderThickness)
|
||||||
|
flex: 1
|
||||||
|
margin-left: - $studiesSidebarMenuWidth
|
||||||
|
max-width: $studiesSidebarMenuWidth
|
||||||
|
order: 1
|
||||||
|
|
||||||
|
&.sidebar-open
|
||||||
|
margin-left: 0
|
||||||
|
|
||||||
|
.mainContent
|
||||||
|
flex: 1
|
||||||
|
height: 100%
|
||||||
|
order: 2
|
||||||
|
overflow: hidden
|
||||||
|
transition($sidebarTransition)
|
||||||
|
width: 100%
|
||||||
|
|
||||||
|
.sidebar-right
|
||||||
|
flex: 1
|
||||||
|
margin-right: - $rightSidebarMenuWidth
|
||||||
|
max-width: $rightSidebarMenuWidth
|
||||||
|
order: 3
|
||||||
|
position: relative
|
||||||
|
|
||||||
|
&[data-timepoints="3"]
|
||||||
|
margin-right: - ($rightSidebarMenuWidth + 135.5px)
|
||||||
|
max-width: $rightSidebarMenuWidth + 135.5px
|
||||||
|
|
||||||
|
&[data-timepoints="4"]
|
||||||
|
margin-right: - ($rightSidebarMenuWidth + 270px)
|
||||||
|
max-width: $rightSidebarMenuWidth + 270px
|
||||||
|
|
||||||
|
&.sidebar-open
|
||||||
|
margin-right: 0
|
||||||
|
|
||||||
|
.studiesListedChanger
|
||||||
|
theme('border-bottom', '%s solid $uiBorderColor' % $uiBorderThickness)
|
||||||
|
padding: 20px 10px
|
||||||
|
text-align: center
|
||||||
15
OHIFViewer-react/src/head.html
Normal file
15
OHIFViewer-react/src/head.html
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title>OHIF Viewer</title>
|
||||||
|
<meta name="description" content="Open Health Imaging Foundation DICOM Viewer">
|
||||||
|
<meta name="viewport" content="width=device-width,initial-scale=1.0,minimum-scale=1,maximum-scale=1,user-scalable=no">
|
||||||
|
<link rel="shortcut icon" type="image/png" href="/images/logo.png" sizes="16x16 32x32 64x64">
|
||||||
|
<!--Best display for mobile devices-->
|
||||||
|
<meta http-equiv="cleartype" content="on">
|
||||||
|
<meta name="MobileOptimized" content="320">
|
||||||
|
<meta name="HandheldFriendly" content="True">
|
||||||
|
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="root"></div>
|
||||||
|
</body>
|
||||||
22
OHIFViewer-react/src/index.js
Normal file
22
OHIFViewer-react/src/index.js
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import ReactDOM from 'react-dom';
|
||||||
|
import { BrowserRouter } from 'react-router-dom';
|
||||||
|
import { Provider } from 'react-redux';
|
||||||
|
import { createStore } from 'redux';
|
||||||
|
import App from './App.js';
|
||||||
|
import { OHIF } from 'ohif-core';
|
||||||
|
import './config';
|
||||||
|
|
||||||
|
const store = createStore(OHIF.redux.combinedReducer);
|
||||||
|
|
||||||
|
// TODO[react] Use a provider when the whole tree is React
|
||||||
|
window.store = store;
|
||||||
|
|
||||||
|
ReactDOM.render(
|
||||||
|
<Provider store={store}>
|
||||||
|
<BrowserRouter>
|
||||||
|
<App store={store}/>
|
||||||
|
</BrowserRouter>
|
||||||
|
</Provider>,
|
||||||
|
document.getElementById('root')
|
||||||
|
);
|
||||||
3
OHIFViewer-react/src/log.js
Normal file
3
OHIFViewer-react/src/log.js
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
import loglevel from 'loglevel';
|
||||||
|
log = loglevel.getLogger('OHIFViewer');
|
||||||
|
log.setLevel('info');
|
||||||
1
OHIFViewer-react/src/sha.js
Normal file
1
OHIFViewer-react/src/sha.js
Normal file
@ -0,0 +1 @@
|
|||||||
|
export default '7b66dec163174eae2f6aca133f740529c8b4d08c';
|
||||||
1
OHIFViewer-react/src/version.js
Normal file
1
OHIFViewer-react/src/version.js
Normal file
@ -0,0 +1 @@
|
|||||||
|
export default '0.0.0';
|
||||||
99
OHIFViewer-react/src/viewer/viewer.js
Normal file
99
OHIFViewer-react/src/viewer/viewer.js
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
import React, { Component } from "react";
|
||||||
|
import PropTypes from "prop-types";
|
||||||
|
import cornerstone from 'cornerstone-core';
|
||||||
|
import cornerstoneTools from 'cornerstone-tools';
|
||||||
|
import { OHIF } from 'ohif-core';
|
||||||
|
import { CineDialog } from 'react-viewerbase';
|
||||||
|
import FlexboxLayout from '../flexboxLayout/flexboxLayout.js';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inits OHIF Hanging Protocol's onReady.
|
||||||
|
* It waits for OHIF Hanging Protocol to be ready to instantiate the ProtocolEngine
|
||||||
|
* Hanging Protocol will use OHIF LayoutManager to render viewports properly
|
||||||
|
*/
|
||||||
|
/*const initHangingProtocol = () => {
|
||||||
|
// When Hanging Protocol is ready
|
||||||
|
HP.ProtocolStore.onReady(() => {
|
||||||
|
|
||||||
|
// Gets all StudyMetadata objects: necessary for Hanging Protocol to access study metadata
|
||||||
|
const studyMetadataList = OHIF.viewer.StudyMetadataList.all();
|
||||||
|
|
||||||
|
// Caches Layout Manager: Hanging Protocol uses it for layout management according to current protocol
|
||||||
|
const layoutManager = OHIF.viewerbase.layoutManager;
|
||||||
|
|
||||||
|
// Instantiate StudyMetadataSource: necessary for Hanging Protocol to get study metadata
|
||||||
|
const studyMetadataSource = new OHIF.studies.classes.OHIFStudyMetadataSource();
|
||||||
|
|
||||||
|
// Get prior studies map
|
||||||
|
const studyPriorsMap = OHIF.studylist.functions.getStudyPriorsMap(studyMetadataList);
|
||||||
|
|
||||||
|
// Creates Protocol Engine object with required arguments
|
||||||
|
const ProtocolEngine = new HP.ProtocolEngine(layoutManager, studyMetadataList, studyPriorsMap, studyMetadataSource);
|
||||||
|
|
||||||
|
// Sets up Hanging Protocol engine
|
||||||
|
HP.setEngine(ProtocolEngine);
|
||||||
|
});
|
||||||
|
};*/
|
||||||
|
|
||||||
|
|
||||||
|
OHIF.viewer.defaultTool = {
|
||||||
|
left: 'wwwc',
|
||||||
|
right: 'zoom',
|
||||||
|
middle: 'pan'
|
||||||
|
};
|
||||||
|
|
||||||
|
OHIF.viewer.refLinesEnabled = true;
|
||||||
|
OHIF.viewer.cine = {
|
||||||
|
framesPerSecond: 24,
|
||||||
|
loop: true
|
||||||
|
};
|
||||||
|
|
||||||
|
const viewportUtils = OHIF.viewerbase.viewportUtils;
|
||||||
|
|
||||||
|
OHIF.viewer.functionList = {
|
||||||
|
toggleCineDialog: viewportUtils.toggleCineDialog,
|
||||||
|
toggleCinePlay: viewportUtils.toggleCinePlay,
|
||||||
|
clearTools: viewportUtils.clearTools,
|
||||||
|
resetViewport: viewportUtils.resetViewport,
|
||||||
|
invert: viewportUtils.invert
|
||||||
|
};
|
||||||
|
|
||||||
|
// Create the synchronizer used to update reference lines
|
||||||
|
OHIF.viewer.updateImageSynchronizer = new cornerstoneTools.Synchronizer('cornerstonenewimage', cornerstoneTools.updateImageSynchronizer);
|
||||||
|
|
||||||
|
OHIF.viewer.metadataProvider = new OHIF.cornerstone.MetadataProvider();
|
||||||
|
|
||||||
|
// Metadata configuration
|
||||||
|
const metadataProvider = OHIF.viewer.metadataProvider;
|
||||||
|
cornerstone.metaData.addProvider(metadataProvider.provider.bind(metadataProvider));
|
||||||
|
|
||||||
|
class Viewer extends Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
leftSidebar: 'studies',
|
||||||
|
rightSidebar: 'measurements',
|
||||||
|
studies: this.props.studies
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (<>
|
||||||
|
<div className='viewerDialogs'>
|
||||||
|
{/*<CineDialog/>*/}
|
||||||
|
</div>
|
||||||
|
<div id="viewer" className='Viewer'>
|
||||||
|
{/*<ToolbarSection/>*/}
|
||||||
|
<FlexboxLayout studies={this.state.studies}/>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Viewer.propTypes = {
|
||||||
|
studies: PropTypes.array
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Viewer;
|
||||||
13
OHIFViewer-react/src/viewer/viewer.styl
Normal file
13
OHIFViewer-react/src/viewer/viewer.styl
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
@import "{ohif:viewerbase}/app"
|
||||||
|
|
||||||
|
body
|
||||||
|
background-color: black
|
||||||
|
|
||||||
|
#viewer
|
||||||
|
background-color: black
|
||||||
|
height: 100%
|
||||||
|
width: 100%
|
||||||
|
|
||||||
|
.loadingTextDiv
|
||||||
|
theme('color', '$textSecondaryColor')
|
||||||
|
font-size: 30px
|
||||||
10139
OHIFViewer-react/yarn-error.log
Normal file
10139
OHIFViewer-react/yarn-error.log
Normal file
File diff suppressed because it is too large
Load Diff
10103
OHIFViewer-react/yarn.lock
Normal file
10103
OHIFViewer-react/yarn.lock
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user