feat(react-measurements): Add measurement table - WIP
This commit is contained in:
parent
e1f1d9b630
commit
84e2f86a76
@ -110,6 +110,7 @@
|
|||||||
"ohif-dicom-html-extension": "^0.0.2",
|
"ohif-dicom-html-extension": "^0.0.2",
|
||||||
"ohif-dicom-microscopy-extension": "^0.0.5",
|
"ohif-dicom-microscopy-extension": "^0.0.5",
|
||||||
"ohif-dicom-pdf-extension": "^0.0.5",
|
"ohif-dicom-pdf-extension": "^0.0.5",
|
||||||
|
"moment": "^2.24.0",
|
||||||
"oidc-client": "^1.6.1",
|
"oidc-client": "^1.6.1",
|
||||||
"prop-types": "^15.6.2",
|
"prop-types": "^15.6.2",
|
||||||
"react-bootstrap-modal": "^4.2.0",
|
"react-bootstrap-modal": "^4.2.0",
|
||||||
|
|||||||
@ -5,6 +5,7 @@ body,
|
|||||||
height: 100%;
|
height: 100%;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
|
|||||||
@ -19,11 +19,11 @@ import {
|
|||||||
reducer as oidcReducer
|
reducer as oidcReducer
|
||||||
} from 'redux-oidc';
|
} from 'redux-oidc';
|
||||||
import cornerstoneWADOImageLoader from 'cornerstone-wado-image-loader';
|
import cornerstoneWADOImageLoader from 'cornerstone-wado-image-loader';
|
||||||
|
import './App.css';
|
||||||
|
|
||||||
const { ExtensionManager } = OHIF.extensions;
|
const { ExtensionManager } = OHIF.extensions;
|
||||||
|
|
||||||
//import Icons from "./images/icons.svg"
|
//import Icons from "./images/icons.svg"
|
||||||
|
|
||||||
const Icons = '/icons.svg';
|
const Icons = '/icons.svg';
|
||||||
|
|
||||||
const { reducers, localStorage } = OHIF.redux;
|
const { reducers, localStorage } = OHIF.redux;
|
||||||
|
|||||||
@ -0,0 +1,48 @@
|
|||||||
|
import { connect } from 'react-redux';
|
||||||
|
import { MeasurementTable } from 'react-viewerbase';
|
||||||
|
import moment from 'moment';
|
||||||
|
|
||||||
|
function convertTimepointsToTableData(timepoints) {
|
||||||
|
if (!timepoints || !timepoints.length) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
label: 'Study date:',
|
||||||
|
date: moment(timepoints[0].latestDate).format('DD-MMM-YY')
|
||||||
|
}
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
function convertMeasurementsToTableData(measurements) {
|
||||||
|
const tableData = [
|
||||||
|
{
|
||||||
|
groupName: 'Measurements',
|
||||||
|
measurements: []
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
if (!measurements || !measurements.length) {
|
||||||
|
return tableData;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Add measurements into tableData
|
||||||
|
|
||||||
|
return tableData;
|
||||||
|
}
|
||||||
|
|
||||||
|
const mapStateToProps = state => {
|
||||||
|
const { timepoints, measurements } = state;
|
||||||
|
return {
|
||||||
|
timepoints: convertTimepointsToTableData(timepoints.timepoints),
|
||||||
|
measurementCollection: convertMeasurementsToTableData(measurements)
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const ConnectedMeasurementTable = connect(
|
||||||
|
mapStateToProps,
|
||||||
|
null
|
||||||
|
)(MeasurementTable);
|
||||||
|
|
||||||
|
export default ConnectedMeasurementTable;
|
||||||
@ -2,89 +2,107 @@ import React, { Component } from 'react';
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import ConnectedStudyBrowser from './ConnectedStudyBrowser.js';
|
import ConnectedStudyBrowser from './ConnectedStudyBrowser.js';
|
||||||
import ViewerMain from './ViewerMain.js';
|
import ViewerMain from './ViewerMain.js';
|
||||||
|
import ConnectedMeasurementTable from './ConnectedMeasurementTable';
|
||||||
import './FlexboxLayout.css';
|
import './FlexboxLayout.css';
|
||||||
|
|
||||||
class FlexboxLayout extends Component {
|
class FlexboxLayout extends Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
studies: PropTypes.array.isRequired,
|
studies: PropTypes.array.isRequired,
|
||||||
leftSidebarOpen: PropTypes.bool.isRequired,
|
leftSidebarOpen: PropTypes.bool.isRequired,
|
||||||
rightSidebarOpen: PropTypes.bool.isRequired,
|
rightSidebarOpen: PropTypes.bool.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
state = {
|
state = {
|
||||||
studiesForBrowser: []
|
studiesForBrowser: []
|
||||||
};
|
};
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
const studiesForBrowser = this.getStudiesForBrowser();
|
const studiesForBrowser = this.getStudiesForBrowser();
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
studiesForBrowser
|
studiesForBrowser
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
getStudiesForBrowser = () => {
|
getStudiesForBrowser = () => {
|
||||||
const { studies } = this.props;
|
const { studies } = this.props;
|
||||||
|
|
||||||
// TODO[react]:
|
// TODO[react]:
|
||||||
// - Add sorting of display sets
|
// - Add sorting of display sets
|
||||||
// - Add useMiddleSeriesInstanceAsThumbnail
|
// - Add useMiddleSeriesInstanceAsThumbnail
|
||||||
// - Add showStackLoadingProgressBar option
|
// - Add showStackLoadingProgressBar option
|
||||||
return studies.map((study) => {
|
return studies.map(study => {
|
||||||
const { studyInstanceUid } = study;
|
const { studyInstanceUid } = study;
|
||||||
|
|
||||||
const thumbnails = study.displaySets.map((displaySet) => {
|
const thumbnails = study.displaySets.map(displaySet => {
|
||||||
const { displaySetInstanceUid, seriesDescription, seriesNumber, instanceNumber, numImageFrames } = displaySet;
|
const {
|
||||||
|
displaySetInstanceUid,
|
||||||
|
seriesDescription,
|
||||||
|
seriesNumber,
|
||||||
|
instanceNumber,
|
||||||
|
numImageFrames
|
||||||
|
} = displaySet;
|
||||||
|
|
||||||
|
let imageId;
|
||||||
|
|
||||||
let imageId;
|
if (displaySet.images && displaySet.images.length) {
|
||||||
|
imageId = displaySet.images[0].getImageId();
|
||||||
if (displaySet.images && displaySet.images.length) {
|
|
||||||
imageId = displaySet.images[0].getImageId();
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
imageId,
|
|
||||||
displaySetInstanceUid,
|
|
||||||
seriesDescription,
|
|
||||||
seriesNumber,
|
|
||||||
instanceNumber,
|
|
||||||
numImageFrames,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
|
||||||
studyInstanceUid,
|
|
||||||
thumbnails
|
|
||||||
};
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
let mainContentClassName = "main-content"
|
|
||||||
if (this.props.leftSidebarOpen) {
|
|
||||||
mainContentClassName += ' sidebar-left-open';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.props.rightSidebarOpen) {
|
return {
|
||||||
mainContentClassName += ' sidebar-right-open';
|
imageId,
|
||||||
}
|
displaySetInstanceUid,
|
||||||
|
seriesDescription,
|
||||||
|
seriesNumber,
|
||||||
|
instanceNumber,
|
||||||
|
numImageFrames
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
// TODO[react]: Add measurementLightTable
|
return {
|
||||||
return (
|
studyInstanceUid,
|
||||||
<div className="FlexboxLayout">
|
thumbnails
|
||||||
<div className={this.props.leftSidebarOpen ? "sidebar-menu sidebar-left sidebar-open" : "sidebar-menu sidebar-left"}>
|
};
|
||||||
<ConnectedStudyBrowser studies={this.state.studiesForBrowser}/>
|
});
|
||||||
</div>
|
};
|
||||||
<div className={mainContentClassName}>
|
|
||||||
<ViewerMain studies={this.props.studies}/>
|
render() {
|
||||||
</div>
|
let mainContentClassName = 'main-content';
|
||||||
{/* <div className={this.props.rightSidebarOpen ? "sidebar-menu sidebar-right sidebar-open" : "sidebar-menu sidebar-right"}>
|
if (this.props.leftSidebarOpen) {
|
||||||
{{> measurementLightTable (clone this)}}
|
mainContentClassName += ' sidebar-left-open';
|
||||||
</div> */}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.props.rightSidebarOpen) {
|
||||||
|
mainContentClassName += ' sidebar-right-open';
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO[react]: Add measurementLightTable
|
||||||
|
return (
|
||||||
|
<div className="FlexboxLayout">
|
||||||
|
<div
|
||||||
|
className={
|
||||||
|
this.props.leftSidebarOpen
|
||||||
|
? 'sidebar-menu sidebar-left sidebar-open'
|
||||||
|
: 'sidebar-menu sidebar-left'
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<ConnectedStudyBrowser studies={this.state.studiesForBrowser} />
|
||||||
|
</div>
|
||||||
|
<div className={mainContentClassName}>
|
||||||
|
<ViewerMain studies={this.props.studies} />
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className={
|
||||||
|
this.props.rightSidebarOpen
|
||||||
|
? 'sidebar-menu sidebar-right sidebar-open'
|
||||||
|
: 'sidebar-menu sidebar-right'
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<ConnectedMeasurementTable />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default FlexboxLayout;
|
export default FlexboxLayout;
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import React, { Component } from "react";
|
import React, { Component } from 'react';
|
||||||
import ConnectedToolbarSection from './ConnectedToolbarSection';
|
import ConnectedToolbarSection from './ConnectedToolbarSection';
|
||||||
import ConnectedLayoutButton from './ConnectedLayoutButton';
|
import ConnectedLayoutButton from './ConnectedLayoutButton';
|
||||||
import ConnectedCineDialog from './ConnectedCineDialog.js';
|
import ConnectedCineDialog from './ConnectedCineDialog.js';
|
||||||
@ -10,68 +10,99 @@ import './ToolbarRow.css';
|
|||||||
const Icons = '/icons.svg';
|
const Icons = '/icons.svg';
|
||||||
|
|
||||||
class ToolbarRow extends Component {
|
class ToolbarRow extends Component {
|
||||||
state = {
|
state = {
|
||||||
cineDialogOpen: false
|
cineDialogOpen: false
|
||||||
|
};
|
||||||
|
|
||||||
|
static propTypes = {
|
||||||
|
leftSidebarOpen: PropTypes.bool.isRequired,
|
||||||
|
rightSidebarOpen: PropTypes.bool.isRequired,
|
||||||
|
setLeftSidebarOpen: PropTypes.func,
|
||||||
|
setRightSidebarOpen: PropTypes.func
|
||||||
|
};
|
||||||
|
|
||||||
|
static defaultProps = {
|
||||||
|
leftSidebarOpen: false,
|
||||||
|
rightSidebarOpen: false
|
||||||
|
};
|
||||||
|
|
||||||
|
onLeftSidebarValueChanged = value => {
|
||||||
|
this.props.setLeftSidebarOpen(!!value);
|
||||||
|
};
|
||||||
|
|
||||||
|
onRightSidebarValueChanged = value => {
|
||||||
|
this.props.setRightSidebarOpen(!!value);
|
||||||
|
};
|
||||||
|
|
||||||
|
onClickCineToolbarButton = () => {
|
||||||
|
this.setState({
|
||||||
|
cineDialogOpen: !this.state.cineDialogOpen
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const leftSidebarToggle = [
|
||||||
|
{
|
||||||
|
value: 'studies',
|
||||||
|
svgLink: `${Icons}#icon-studies`,
|
||||||
|
svgWidth: 15,
|
||||||
|
svgHeight: 13,
|
||||||
|
bottomLabel: 'Series'
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
const rightSidebarToggle = [
|
||||||
|
{
|
||||||
|
value: 'measurements',
|
||||||
|
svgLink: `${Icons}#icon-measurements-lesions`,
|
||||||
|
svgWidth: 15,
|
||||||
|
svgHeight: 13,
|
||||||
|
bottomLabel: 'Measurements'
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
const leftSidebarValue = this.props.leftSidebarOpen
|
||||||
|
? leftSidebarToggle[0].value
|
||||||
|
: null;
|
||||||
|
|
||||||
|
const rightSidebarValue = this.props.rightSidebarOpen
|
||||||
|
? rightSidebarToggle[0].value
|
||||||
|
: null;
|
||||||
|
|
||||||
|
const cineDialogContainerStyle = {
|
||||||
|
display: this.state.cineDialogOpen ? 'inline-block' : 'none'
|
||||||
};
|
};
|
||||||
|
|
||||||
static propTypes = {
|
return (
|
||||||
leftSidebarOpen: PropTypes.bool.isRequired,
|
<div className="ToolbarRow">
|
||||||
rightSidebarOpen: PropTypes.bool.isRequired,
|
<div className="pull-left m-t-1 p-y-1" style={{ padding: '10px' }}>
|
||||||
setLeftSidebarOpen: PropTypes.func,
|
<RoundedButtonGroup
|
||||||
setRightSidebarOpen: PropTypes.func
|
options={leftSidebarToggle}
|
||||||
};
|
value={leftSidebarValue}
|
||||||
|
onValueChanged={this.onLeftSidebarValueChanged}
|
||||||
static defaultProps = {
|
/>
|
||||||
leftSidebarOpen: false,
|
</div>
|
||||||
rightSidebarOpen: false
|
<ConnectedToolbarSection />
|
||||||
};
|
<ConnectedLayoutButton />
|
||||||
|
<ToolbarButton
|
||||||
onLeftSidebarValueChanged = (value) => {
|
active={this.state.cineDialogOpen}
|
||||||
this.props.setLeftSidebarOpen(!!value);
|
onClick={this.onClickCineToolbarButton}
|
||||||
}
|
text={'CINE'}
|
||||||
|
iconClasses={'fa fa-youtube'}
|
||||||
onClickCineToolbarButton = () => {
|
/>
|
||||||
this.setState({
|
<div className="CineDialogContainer" style={cineDialogContainerStyle}>
|
||||||
cineDialogOpen: !this.state.cineDialogOpen
|
<ConnectedCineDialog />
|
||||||
});
|
</div>
|
||||||
}
|
<div className="pull-right m-t-1 rm-x-1" style={{ marginLeft: 'auto' }}>
|
||||||
|
<RoundedButtonGroup
|
||||||
render() {
|
options={rightSidebarToggle}
|
||||||
const leftSidebarToggle = [{
|
value={rightSidebarValue}
|
||||||
value: 'studies',
|
onValueChanged={this.onRightSidebarValueChanged}
|
||||||
svgLink: `${Icons}#icon-studies`,
|
/>
|
||||||
svgWidth: 15,
|
</div>
|
||||||
svgHeight: 13,
|
</div>
|
||||||
bottomLabel: 'Series',
|
);
|
||||||
}];
|
}
|
||||||
|
|
||||||
const leftSidebarValue = this.props.leftSidebarOpen ? leftSidebarToggle[0].value : null;
|
|
||||||
|
|
||||||
const cineDialogContainerStyle = {
|
|
||||||
display: this.state.cineDialogOpen ? 'inline-block' : 'none'
|
|
||||||
}
|
|
||||||
|
|
||||||
return (<div className="ToolbarRow">
|
|
||||||
<div className="pull-left m-t-1 p-y-1" style={{ padding: '10px' }}>
|
|
||||||
<RoundedButtonGroup options={leftSidebarToggle} value={leftSidebarValue} onValueChanged={this.onLeftSidebarValueChanged}/>
|
|
||||||
</div>
|
|
||||||
<ConnectedToolbarSection/>
|
|
||||||
<ConnectedLayoutButton/>
|
|
||||||
<ToolbarButton
|
|
||||||
active={this.state.cineDialogOpen}
|
|
||||||
onClick={this.onClickCineToolbarButton}
|
|
||||||
text={'CINE'}
|
|
||||||
iconClasses={'fa fa-youtube'}
|
|
||||||
/>
|
|
||||||
<div className='CineDialogContainer' style={cineDialogContainerStyle}>
|
|
||||||
<ConnectedCineDialog/>
|
|
||||||
</div>
|
|
||||||
<div className="pull-right m-t-1 rm-x-1">
|
|
||||||
{/* > roundedButtonGroup rightSidebarToggleButtonData */}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default ToolbarRow;
|
export default ToolbarRow;
|
||||||
|
|||||||
@ -3,12 +3,15 @@ import PropTypes from 'prop-types';
|
|||||||
//import OHIF from 'ohif-core';
|
//import OHIF from 'ohif-core';
|
||||||
//import { CineDialog } from 'react-viewerbase';
|
//import { CineDialog } from 'react-viewerbase';
|
||||||
|
|
||||||
|
import OHIF from 'ohif-core';
|
||||||
|
import moment from 'moment';
|
||||||
import WhiteLabellingContext from '../WhiteLabellingContext.js';
|
import WhiteLabellingContext from '../WhiteLabellingContext.js';
|
||||||
import ConnectedHeader from './ConnectedHeader.js';
|
import ConnectedHeader from './ConnectedHeader.js';
|
||||||
import ConnectedFlexboxLayout from './ConnectedFlexboxLayout.js';
|
import ConnectedFlexboxLayout from './ConnectedFlexboxLayout.js';
|
||||||
import ConnectedToolbarRow from './ConnectedToolbarRow.js';
|
import ConnectedToolbarRow from './ConnectedToolbarRow.js';
|
||||||
import ConnectedStudyLoadingMonitor from './ConnectedStudyLoadingMonitor.js';
|
import ConnectedStudyLoadingMonitor from './ConnectedStudyLoadingMonitor.js';
|
||||||
import StudyPrefetcher from '../components/StudyPrefetcher.js';
|
import StudyPrefetcher from '../components/StudyPrefetcher.js';
|
||||||
|
import measurementTools from '../measurementTools';
|
||||||
import './Viewer.css';
|
import './Viewer.css';
|
||||||
/**
|
/**
|
||||||
* Inits OHIF Hanging Protocol's onReady.
|
* Inits OHIF Hanging Protocol's onReady.
|
||||||
@ -51,6 +54,117 @@ class Viewer extends Component {
|
|||||||
studies: PropTypes.array
|
studies: PropTypes.array
|
||||||
};
|
};
|
||||||
|
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
const retrieveMeasurements = (patientId, timepointIds) => {
|
||||||
|
OHIF.log.info('retrieveMeasurements');
|
||||||
|
// TODO: Retrieve the measurements from the latest available SR
|
||||||
|
return Promise.resolve();
|
||||||
|
};
|
||||||
|
|
||||||
|
const storeMeasurements = (measurementData, timepointIds) => {
|
||||||
|
OHIF.log.info('storeMeasurements');
|
||||||
|
// TODO: Store the measurements into a new SR sent to the active server
|
||||||
|
return Promise.resolve();
|
||||||
|
};
|
||||||
|
|
||||||
|
const retrieveTimepoints = filter => {
|
||||||
|
OHIF.log.info('retrieveTimepoints');
|
||||||
|
|
||||||
|
// Get the earliest and latest study date
|
||||||
|
let earliestDate = new Date().toISOString();
|
||||||
|
let latestDate = new Date().toISOString();
|
||||||
|
if (props.studies) {
|
||||||
|
latestDate = new Date('1000-01-01').toISOString();
|
||||||
|
props.studies.forEach(study => {
|
||||||
|
const studyDate = moment(study.studyDate, 'YYYYMMDD').toISOString();
|
||||||
|
if (studyDate < earliestDate) {
|
||||||
|
earliestDate = studyDate;
|
||||||
|
}
|
||||||
|
if (studyDate > latestDate) {
|
||||||
|
latestDate = studyDate;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return a generic timepoint
|
||||||
|
return Promise.resolve([
|
||||||
|
{
|
||||||
|
timepointType: 'baseline',
|
||||||
|
timepointId: 'TimepointId',
|
||||||
|
studyInstanceUids: props.studyInstanceUids,
|
||||||
|
patientId: filter.patientId,
|
||||||
|
earliestDate,
|
||||||
|
latestDate,
|
||||||
|
isLocked: false
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
};
|
||||||
|
|
||||||
|
const storeTimepoints = timepointData => {
|
||||||
|
OHIF.log.info('storeTimepoints');
|
||||||
|
return Promise.resolve();
|
||||||
|
};
|
||||||
|
|
||||||
|
const updateTimepoint = (timepointData, query) => {
|
||||||
|
OHIF.log.info('updateTimepoint');
|
||||||
|
return Promise.resolve();
|
||||||
|
};
|
||||||
|
|
||||||
|
const removeTimepoint = timepointId => {
|
||||||
|
OHIF.log.info('removeTimepoint');
|
||||||
|
return Promise.resolve();
|
||||||
|
};
|
||||||
|
|
||||||
|
const disassociateStudy = (timepointIds, studyInstanceUid) => {
|
||||||
|
OHIF.log.info('disassociateStudy');
|
||||||
|
return Promise.resolve();
|
||||||
|
};
|
||||||
|
|
||||||
|
OHIF.measurements.MeasurementApi.setConfiguration({
|
||||||
|
measurementTools,
|
||||||
|
dataExchange: {
|
||||||
|
retrieve: retrieveMeasurements,
|
||||||
|
store: storeMeasurements
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
OHIF.measurements.TimepointApi.setConfiguration({
|
||||||
|
dataExchange: {
|
||||||
|
retrieve: retrieveTimepoints,
|
||||||
|
store: storeTimepoints,
|
||||||
|
remove: removeTimepoint,
|
||||||
|
update: updateTimepoint,
|
||||||
|
disassociate: disassociateStudy
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
const { studies } = this.props;
|
||||||
|
const { TimepointApi, MeasurementApi } = OHIF.measurements;
|
||||||
|
|
||||||
|
// TODO: Get the Redux store from somewhere else
|
||||||
|
const { store } = window;
|
||||||
|
|
||||||
|
const timepointApi = new TimepointApi(store, 'TimepointId');
|
||||||
|
const measurementApi = new MeasurementApi(store, timepointApi);
|
||||||
|
const apis = {
|
||||||
|
timepointApi,
|
||||||
|
measurementApi
|
||||||
|
};
|
||||||
|
|
||||||
|
Object.assign(OHIF.viewer, apis);
|
||||||
|
|
||||||
|
const patientId = studies[0] && studies[0].patientId;
|
||||||
|
timepointApi.retrieveTimepoints({ patientId });
|
||||||
|
|
||||||
|
// TODO: Retrieve measurements and sync them with tool data
|
||||||
|
// measurementApi.retrieveMeasurements(patientId, [currentTimepointId]);
|
||||||
|
// measurementApi.syncMeasurementsAndToolData();
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|||||||
@ -0,0 +1,15 @@
|
|||||||
|
const displayFunction = data => {
|
||||||
|
return data.text || '';
|
||||||
|
};
|
||||||
|
|
||||||
|
export default {
|
||||||
|
id: 'arrowAnnotate',
|
||||||
|
name: 'ArrowAnnotate',
|
||||||
|
toolGroup: 'allTools',
|
||||||
|
cornerstoneToolType: 'arrowAnnotate',
|
||||||
|
options: {
|
||||||
|
measurementTable: {
|
||||||
|
displayFunction
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
@ -0,0 +1,22 @@
|
|||||||
|
const displayFunction = data => {
|
||||||
|
let meanValue = '';
|
||||||
|
if (data.meanStdDev && data.meanStdDev.mean) {
|
||||||
|
meanValue = data.meanStdDev.mean.toFixed(2) + ' HU';
|
||||||
|
}
|
||||||
|
return meanValue;
|
||||||
|
// let meanValue = data.meanStdDev && data.meanStdDev.mean || 0;
|
||||||
|
// return numberWithCommas(meanValue).toFixed(2) + ' HU';
|
||||||
|
//return data.meanStdDev.mean.toFixed(2);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default {
|
||||||
|
id: 'ellipticalRoi',
|
||||||
|
name: 'Ellipse',
|
||||||
|
toolGroup: 'allTools',
|
||||||
|
cornerstoneToolType: 'ellipticalRoi',
|
||||||
|
options: {
|
||||||
|
measurementTable: {
|
||||||
|
displayFunction
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
21
Packages-react/ohif-viewer/src/measurementTools/index.js
Normal file
21
Packages-react/ohif-viewer/src/measurementTools/index.js
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import length from './length';
|
||||||
|
import ellipticalRoi from './ellipticalRoi';
|
||||||
|
import rectangleRoi from './rectangleRoi';
|
||||||
|
import simpleAngle from './simpleAngle';
|
||||||
|
import arrowAnnotate from './arrowAnnotate';
|
||||||
|
|
||||||
|
const trackedTools = [
|
||||||
|
length,
|
||||||
|
ellipticalRoi,
|
||||||
|
rectangleRoi,
|
||||||
|
simpleAngle,
|
||||||
|
arrowAnnotate
|
||||||
|
];
|
||||||
|
|
||||||
|
export default [
|
||||||
|
{
|
||||||
|
id: 'allTools',
|
||||||
|
name: 'Measurements',
|
||||||
|
childTools: trackedTools
|
||||||
|
}
|
||||||
|
];
|
||||||
19
Packages-react/ohif-viewer/src/measurementTools/length.js
Normal file
19
Packages-react/ohif-viewer/src/measurementTools/length.js
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
const displayFunction = data => {
|
||||||
|
let lengthValue = '';
|
||||||
|
if (data.length) {
|
||||||
|
lengthValue = data.length.toFixed(2) + ' mm';
|
||||||
|
}
|
||||||
|
return lengthValue;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default {
|
||||||
|
id: 'length',
|
||||||
|
name: 'Length',
|
||||||
|
toolGroup: 'allTools',
|
||||||
|
cornerstoneToolType: 'length',
|
||||||
|
options: {
|
||||||
|
measurementTable: {
|
||||||
|
displayFunction
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
@ -0,0 +1,19 @@
|
|||||||
|
const displayFunction = data => {
|
||||||
|
let meanValue = '';
|
||||||
|
if (data.meanStdDev && data.meanStdDev.mean) {
|
||||||
|
meanValue = data.meanStdDev.mean.toFixed(2) + ' HU';
|
||||||
|
}
|
||||||
|
return meanValue;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default {
|
||||||
|
id: 'rectangleRoi',
|
||||||
|
name: 'Rectangle',
|
||||||
|
toolGroup: 'allTools',
|
||||||
|
cornerstoneToolType: 'rectangleRoi',
|
||||||
|
options: {
|
||||||
|
measurementTable: {
|
||||||
|
displayFunction
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
@ -0,0 +1,19 @@
|
|||||||
|
const displayFunction = data => {
|
||||||
|
let text = '';
|
||||||
|
if (data.rAngle) {
|
||||||
|
text = data.rAngle.toFixed(2) + String.fromCharCode(parseInt('00B0', 16));
|
||||||
|
}
|
||||||
|
return text;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default {
|
||||||
|
id: 'simpleAngle',
|
||||||
|
name: 'SimpleAngle',
|
||||||
|
toolGroup: 'allTools',
|
||||||
|
cornerstoneToolType: 'simpleAngle',
|
||||||
|
options: {
|
||||||
|
measurementTable: {
|
||||||
|
displayFunction
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
Loading…
Reference in New Issue
Block a user