Initial commit from prod-batam
This commit is contained in:
147
extensions/cornerstone/src/panels/PanelMeasurement.tsx
Normal file
147
extensions/cornerstone/src/panels/PanelMeasurement.tsx
Normal file
@@ -0,0 +1,147 @@
|
||||
import React, { useEffect, useRef } from 'react';
|
||||
import { useViewportGrid } from '@ohif/ui';
|
||||
import { MeasurementTable } from '@ohif/ui-next';
|
||||
import debounce from 'lodash.debounce';
|
||||
import { useMeasurements } from '../hooks/useMeasurements';
|
||||
import { showLabelAnnotationPopup, colorPickerDialog } from '@ohif/extension-default';
|
||||
|
||||
export default function PanelMeasurementTable({
|
||||
servicesManager,
|
||||
customHeader,
|
||||
measurementFilter,
|
||||
}: withAppTypes): React.ReactNode {
|
||||
const measurementsPanelRef = useRef(null);
|
||||
|
||||
const [viewportGrid] = useViewportGrid();
|
||||
const { measurementService, customizationService, uiDialogService } = servicesManager.services;
|
||||
|
||||
const displayMeasurements = useMeasurements(servicesManager, {
|
||||
measurementFilter,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (displayMeasurements.length > 0) {
|
||||
debounce(() => {
|
||||
measurementsPanelRef.current.scrollTop = measurementsPanelRef.current.scrollHeight;
|
||||
}, 300)();
|
||||
}
|
||||
}, [displayMeasurements.length]);
|
||||
|
||||
const onMeasurementItemClickHandler = (uid: string, isActive: boolean) => {
|
||||
if (isActive) {
|
||||
return;
|
||||
}
|
||||
|
||||
const measurements = [...displayMeasurements];
|
||||
const measurement = measurements.find(m => m.uid === uid);
|
||||
|
||||
measurements.forEach(m => (m.isActive = m.uid !== uid ? false : true));
|
||||
measurement.isActive = true;
|
||||
};
|
||||
|
||||
const jumpToImage = (uid: string) => {
|
||||
measurementService.jumpToMeasurement(viewportGrid.activeViewportId, uid);
|
||||
onMeasurementItemClickHandler(uid, true);
|
||||
};
|
||||
|
||||
const removeMeasurement = (uid: string) => {
|
||||
measurementService.remove(uid);
|
||||
};
|
||||
|
||||
const renameMeasurement = (uid: string) => {
|
||||
jumpToImage(uid);
|
||||
const labelConfig = customizationService.get('measurementLabels');
|
||||
const measurement = measurementService.getMeasurement(uid);
|
||||
showLabelAnnotationPopup(measurement, uiDialogService, labelConfig).then(val => {
|
||||
measurementService.update(
|
||||
uid,
|
||||
{
|
||||
...val,
|
||||
},
|
||||
true
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
const changeColorMeasurement = (uid: string) => {
|
||||
const { color } = measurementService.getMeasurement(uid);
|
||||
const rgbaColor = {
|
||||
r: color[0],
|
||||
g: color[1],
|
||||
b: color[2],
|
||||
a: color[3] / 255.0,
|
||||
};
|
||||
colorPickerDialog(uiDialogService, rgbaColor, (newRgbaColor, actionId) => {
|
||||
if (actionId === 'cancel') {
|
||||
return;
|
||||
}
|
||||
|
||||
const color = [newRgbaColor.r, newRgbaColor.g, newRgbaColor.b, newRgbaColor.a * 255.0];
|
||||
// segmentationService.setSegmentColor(viewportId, segmentationId, segmentIndex, color);
|
||||
});
|
||||
};
|
||||
|
||||
const toggleLockMeasurement = (uid: string) => {
|
||||
measurementService.toggleLockMeasurement(uid);
|
||||
};
|
||||
|
||||
const toggleVisibilityMeasurement = (uid: string) => {
|
||||
measurementService.toggleVisibilityMeasurement(uid);
|
||||
};
|
||||
|
||||
const measurements = displayMeasurements.filter(
|
||||
dm => dm.measurementType !== measurementService.VALUE_TYPES.POINT && dm.referencedImageId
|
||||
);
|
||||
const additionalFindings = displayMeasurements.filter(
|
||||
dm => dm.measurementType === measurementService.VALUE_TYPES.POINT && dm.referencedImageId
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
className="invisible-scrollbar overflow-y-auto overflow-x-hidden"
|
||||
ref={measurementsPanelRef}
|
||||
data-cy={'trackedMeasurements-panel'}
|
||||
>
|
||||
<MeasurementTable
|
||||
title="Measurements"
|
||||
data={measurements}
|
||||
onClick={jumpToImage}
|
||||
onDelete={removeMeasurement}
|
||||
onToggleVisibility={toggleVisibilityMeasurement}
|
||||
onToggleLocked={toggleLockMeasurement}
|
||||
onRename={renameMeasurement}
|
||||
// onColor={changeColorMeasurement}
|
||||
>
|
||||
<MeasurementTable.Header>
|
||||
{customHeader && (
|
||||
<>
|
||||
{typeof customHeader === 'function'
|
||||
? customHeader({
|
||||
additionalFindings,
|
||||
measurements,
|
||||
})
|
||||
: customHeader}
|
||||
</>
|
||||
)}
|
||||
</MeasurementTable.Header>
|
||||
<MeasurementTable.Body />
|
||||
</MeasurementTable>
|
||||
{additionalFindings.length > 0 && (
|
||||
<MeasurementTable
|
||||
data={additionalFindings}
|
||||
title="Additional Findings"
|
||||
onClick={jumpToImage}
|
||||
onDelete={removeMeasurement}
|
||||
onToggleVisibility={toggleVisibilityMeasurement}
|
||||
onToggleLocked={toggleLockMeasurement}
|
||||
onRename={renameMeasurement}
|
||||
// onColor={changeColorMeasurement}
|
||||
>
|
||||
<MeasurementTable.Body />
|
||||
</MeasurementTable>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
236
extensions/cornerstone/src/panels/PanelSegmentation.tsx
Normal file
236
extensions/cornerstone/src/panels/PanelSegmentation.tsx
Normal file
@@ -0,0 +1,236 @@
|
||||
import React from 'react';
|
||||
import { SegmentationTable } from '@ohif/ui-next';
|
||||
import { useActiveViewportSegmentationRepresentations } from '../hooks/useActiveViewportSegmentationRepresentations';
|
||||
import { metaData } from '@cornerstonejs/core';
|
||||
|
||||
export default function PanelSegmentation({
|
||||
servicesManager,
|
||||
commandsManager,
|
||||
children,
|
||||
}: withAppTypes) {
|
||||
const { customizationService, viewportGridService, displaySetService } = servicesManager.services;
|
||||
|
||||
const { segmentationsWithRepresentations, disabled } =
|
||||
useActiveViewportSegmentationRepresentations({
|
||||
servicesManager,
|
||||
});
|
||||
|
||||
const handlers = {
|
||||
onSegmentationAdd: async () => {
|
||||
const viewportId = viewportGridService.getState().activeViewportId;
|
||||
commandsManager.run('createLabelmapForViewport', { viewportId });
|
||||
},
|
||||
|
||||
onSegmentationClick: (segmentationId: string) => {
|
||||
commandsManager.run('setActiveSegmentation', { segmentationId });
|
||||
},
|
||||
|
||||
onSegmentAdd: segmentationId => {
|
||||
commandsManager.run('addSegment', { segmentationId });
|
||||
},
|
||||
|
||||
onSegmentClick: (segmentationId, segmentIndex) => {
|
||||
commandsManager.run('setActiveSegmentAndCenter', { segmentationId, segmentIndex });
|
||||
},
|
||||
|
||||
onSegmentEdit: (segmentationId, segmentIndex) => {
|
||||
commandsManager.run('editSegmentLabel', { segmentationId, segmentIndex });
|
||||
},
|
||||
|
||||
onSegmentationEdit: segmentationId => {
|
||||
commandsManager.run('editSegmentationLabel', { segmentationId });
|
||||
},
|
||||
|
||||
onSegmentColorClick: (segmentationId, segmentIndex) => {
|
||||
commandsManager.run('editSegmentColor', { segmentationId, segmentIndex });
|
||||
},
|
||||
|
||||
onSegmentDelete: (segmentationId, segmentIndex) => {
|
||||
commandsManager.run('deleteSegment', { segmentationId, segmentIndex });
|
||||
},
|
||||
|
||||
onToggleSegmentVisibility: (segmentationId, segmentIndex, type) => {
|
||||
commandsManager.run('toggleSegmentVisibility', { segmentationId, segmentIndex, type });
|
||||
},
|
||||
|
||||
onToggleSegmentLock: (segmentationId, segmentIndex) => {
|
||||
commandsManager.run('toggleSegmentLock', { segmentationId, segmentIndex });
|
||||
},
|
||||
|
||||
onToggleSegmentationRepresentationVisibility: (segmentationId, type) => {
|
||||
commandsManager.run('toggleSegmentationVisibility', { segmentationId, type });
|
||||
},
|
||||
|
||||
onSegmentationDownload: segmentationId => {
|
||||
commandsManager.run('downloadSegmentation', { segmentationId });
|
||||
},
|
||||
|
||||
storeSegmentation: async segmentationId => {
|
||||
commandsManager.run('storeSegmentation', { segmentationId });
|
||||
},
|
||||
|
||||
onSegmentationDownloadRTSS: segmentationId => {
|
||||
commandsManager.run('downloadRTSS', { segmentationId });
|
||||
},
|
||||
|
||||
setStyle: (segmentationId, type, key, value) => {
|
||||
commandsManager.run('setSegmentationStyle', { segmentationId, type, key, value });
|
||||
},
|
||||
|
||||
toggleRenderInactiveSegmentations: () => {
|
||||
commandsManager.run('toggleRenderInactiveSegmentations');
|
||||
},
|
||||
|
||||
onSegmentationRemoveFromViewport: segmentationId => {
|
||||
commandsManager.run('removeSegmentationFromViewport', { segmentationId });
|
||||
},
|
||||
|
||||
onSegmentationDelete: segmentationId => {
|
||||
commandsManager.run('deleteSegmentation', { segmentationId });
|
||||
},
|
||||
|
||||
setFillAlpha: ({ type }, value) => {
|
||||
commandsManager.run('setFillAlpha', { type, value });
|
||||
},
|
||||
|
||||
setOutlineWidth: ({ type }, value) => {
|
||||
commandsManager.run('setOutlineWidth', { type, value });
|
||||
},
|
||||
|
||||
setRenderFill: ({ type }, value) => {
|
||||
commandsManager.run('setRenderFill', { type, value });
|
||||
},
|
||||
|
||||
setRenderOutline: ({ type }, value) => {
|
||||
commandsManager.run('setRenderOutline', { type, value });
|
||||
},
|
||||
|
||||
setFillAlphaInactive: ({ type }, value) => {
|
||||
commandsManager.run('setFillAlphaInactive', { type, value });
|
||||
},
|
||||
|
||||
getRenderInactiveSegmentations: () => {
|
||||
return commandsManager.run('getRenderInactiveSegmentations');
|
||||
},
|
||||
};
|
||||
|
||||
const { mode: SegmentationTableMode } = customizationService.getCustomization(
|
||||
'PanelSegmentation.tableMode',
|
||||
{
|
||||
id: 'default.segmentationTable.mode',
|
||||
mode: 'collapsed',
|
||||
}
|
||||
);
|
||||
|
||||
// custom onSegmentationAdd if provided
|
||||
const { onSegmentationAdd } = customizationService.getCustomization(
|
||||
'PanelSegmentation.onSegmentationAdd',
|
||||
{
|
||||
id: 'segmentation.onSegmentationAdd',
|
||||
onSegmentationAdd: handlers.onSegmentationAdd,
|
||||
}
|
||||
);
|
||||
|
||||
const { disableEditing } = customizationService.getCustomization(
|
||||
'PanelSegmentation.disableEditing',
|
||||
{
|
||||
id: 'default.disableEditing',
|
||||
disableEditing: false,
|
||||
}
|
||||
);
|
||||
|
||||
const { showAddSegment } = customizationService.getCustomization(
|
||||
'PanelSegmentation.showAddSegment',
|
||||
{
|
||||
id: 'default.showAddSegment',
|
||||
showAddSegment: true,
|
||||
}
|
||||
);
|
||||
|
||||
const exportOptions = segmentationsWithRepresentations.map(({ segmentation }) => {
|
||||
const { representationData, segmentationId } = segmentation;
|
||||
const { Labelmap } = representationData;
|
||||
|
||||
if (!Labelmap) {
|
||||
return {
|
||||
segmentationId,
|
||||
isExportable: true,
|
||||
};
|
||||
}
|
||||
|
||||
const referencedImageIds = Labelmap.referencedImageIds;
|
||||
const firstImageId = referencedImageIds[0];
|
||||
|
||||
const instance = metaData.get('instance', firstImageId);
|
||||
const { SOPInstanceUID, SeriesInstanceUID } = instance;
|
||||
|
||||
const displaySet = displaySetService.getDisplaySetForSOPInstanceUID(
|
||||
SOPInstanceUID,
|
||||
SeriesInstanceUID
|
||||
);
|
||||
const isExportable = displaySet.isReconstructable;
|
||||
|
||||
return {
|
||||
segmentationId,
|
||||
isExportable,
|
||||
};
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
<SegmentationTable
|
||||
disabled={disabled}
|
||||
data={segmentationsWithRepresentations}
|
||||
mode={SegmentationTableMode}
|
||||
title="Segmentations"
|
||||
exportOptions={exportOptions}
|
||||
disableEditing={disableEditing}
|
||||
onSegmentationAdd={onSegmentationAdd}
|
||||
onSegmentationClick={handlers.onSegmentationClick}
|
||||
onSegmentationDelete={handlers.onSegmentationDelete}
|
||||
showAddSegment={showAddSegment}
|
||||
onSegmentAdd={handlers.onSegmentAdd}
|
||||
onSegmentClick={handlers.onSegmentClick}
|
||||
onSegmentEdit={handlers.onSegmentEdit}
|
||||
onSegmentationEdit={handlers.onSegmentationEdit}
|
||||
onSegmentColorClick={handlers.onSegmentColorClick}
|
||||
onSegmentDelete={handlers.onSegmentDelete}
|
||||
onToggleSegmentVisibility={handlers.onToggleSegmentVisibility}
|
||||
onToggleSegmentLock={handlers.onToggleSegmentLock}
|
||||
onToggleSegmentationRepresentationVisibility={
|
||||
handlers.onToggleSegmentationRepresentationVisibility
|
||||
}
|
||||
onSegmentationDownload={handlers.onSegmentationDownload}
|
||||
storeSegmentation={handlers.storeSegmentation}
|
||||
onSegmentationDownloadRTSS={handlers.onSegmentationDownloadRTSS}
|
||||
setStyle={handlers.setStyle}
|
||||
toggleRenderInactiveSegmentations={handlers.toggleRenderInactiveSegmentations}
|
||||
onSegmentationRemoveFromViewport={handlers.onSegmentationRemoveFromViewport}
|
||||
setFillAlpha={handlers.setFillAlpha}
|
||||
setOutlineWidth={handlers.setOutlineWidth}
|
||||
setRenderFill={handlers.setRenderFill}
|
||||
setRenderOutline={handlers.setRenderOutline}
|
||||
setFillAlphaInactive={handlers.setFillAlphaInactive}
|
||||
renderInactiveSegmentations={handlers.getRenderInactiveSegmentations()}
|
||||
>
|
||||
{children}
|
||||
<SegmentationTable.Config />
|
||||
<SegmentationTable.AddSegmentationRow />
|
||||
|
||||
{SegmentationTableMode === 'collapsed' ? (
|
||||
<SegmentationTable.Collapsed>
|
||||
<SegmentationTable.SelectorHeader />
|
||||
<SegmentationTable.AddSegmentRow />
|
||||
<SegmentationTable.Segments />
|
||||
</SegmentationTable.Collapsed>
|
||||
) : (
|
||||
<SegmentationTable.Expanded>
|
||||
<SegmentationTable.Header />
|
||||
{/* <SegmentationTable.AddSegmentRow /> */}
|
||||
<SegmentationTable.Segments />
|
||||
</SegmentationTable.Expanded>
|
||||
)}
|
||||
</SegmentationTable>
|
||||
</>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user