矩形工具更改
continuous-integration/drone/push Build is passing
Details
continuous-integration/drone/push Build is passing
Details
parent
b2645f6423
commit
4ff6416edb
|
@ -563,6 +563,7 @@ import Others from '@/views/trials/trials-panel/reading/dicoms/components/Others
|
|||
import ClinicalData from '@/views/trials/trials-panel/reading/clinical-data'
|
||||
import FusionForm from './FusionForm.vue'
|
||||
import colorMap from './colorMap.vue'
|
||||
import RectangleROITool from './tools/RectangleROITool'
|
||||
const { visibility } = annotation
|
||||
const { ViewportType, Events } = Enums
|
||||
const renderingEngineId = 'myRenderingEngine'
|
||||
|
@ -579,7 +580,7 @@ const {
|
|||
LengthTool,
|
||||
BidirectionalTool,
|
||||
ArrowAnnotateTool,
|
||||
RectangleROITool,
|
||||
// RectangleROITool,
|
||||
PlanarFreehandROITool,
|
||||
CircleROITool,
|
||||
EraserTool,
|
||||
|
|
|
@ -0,0 +1,306 @@
|
|||
|
||||
import {
|
||||
VolumeViewport,
|
||||
utilities as csUtils,
|
||||
} from '@cornerstonejs/core'
|
||||
import * as cornerstoneTools from '@cornerstonejs/tools'
|
||||
const {
|
||||
utilities,
|
||||
annotation,
|
||||
drawing
|
||||
|
||||
} = cornerstoneTools
|
||||
|
||||
const { getAnnotations } = annotation.state
|
||||
const { isAnnotationVisible } = annotation.visibility
|
||||
const { isAnnotationLocked } = annotation.locking
|
||||
|
||||
|
||||
const drawHandlesSvg = drawing.drawHandles
|
||||
const drawRectSvg = drawing.drawRectByCoordinates
|
||||
const drawLinkedTextBoxSvg = drawing.drawLinkedTextBox
|
||||
const { getTextBoxCoordsCanvas } = utilities.drawing
|
||||
|
||||
|
||||
/**
|
||||
* RectangleROIAnnotation let you draw annotations that measures the statistics
|
||||
* such as area, max, mean and stdDev of a Rectangular region of interest.
|
||||
* You can use RectangleROIAnnotation in all perpendicular views (axial, sagittal, coronal).
|
||||
* Note: annotation tools in cornerstone3DTools exists in the exact location
|
||||
* in the physical 3d space, as a result, by default, all annotations that are
|
||||
* drawing in the same frameOfReference will get shared between viewports that
|
||||
* are in the same frameOfReference. RectangleROI tool's text box lines are dynamically
|
||||
* generated based on the viewport's underlying Modality. For instance, if
|
||||
* the viewport is displaying CT, the text box will shown the statistics in Hounsfield units,
|
||||
* and if the viewport is displaying PET, the text box will show the statistics in
|
||||
* SUV units.
|
||||
*
|
||||
* The resulting annotation's data (statistics) and metadata (the
|
||||
* state of the viewport while drawing was happening) will get added to the
|
||||
* ToolState manager and can be accessed from the ToolState by calling getAnnotations
|
||||
* or similar methods.
|
||||
*
|
||||
* ```js
|
||||
* cornerstoneTools.addTool(RectangleROITool)
|
||||
*
|
||||
* const toolGroup = ToolGroupManager.createToolGroup('toolGroupId')
|
||||
*
|
||||
* toolGroup.addTool(RectangleROITool.toolName)
|
||||
*
|
||||
* toolGroup.addViewport('viewportId', 'renderingEngineId')
|
||||
*
|
||||
* toolGroup.setToolActive(RectangleROITool.toolName, {
|
||||
* bindings: [
|
||||
* {
|
||||
* mouseButton: MouseBindings.Primary, // Left Click
|
||||
* },
|
||||
* ],
|
||||
* })
|
||||
* ```
|
||||
*
|
||||
* Read more in the Docs section of the website.
|
||||
*/
|
||||
|
||||
class RectangleROITool extends cornerstoneTools.RectangleROITool {
|
||||
constructor(
|
||||
toolProps,
|
||||
defaultToolProps
|
||||
) {
|
||||
super(toolProps, defaultToolProps);
|
||||
}
|
||||
|
||||
/**
|
||||
* it is used to draw the rectangleROI annotation in each
|
||||
* request animation frame. It calculates the updated cached statistics if
|
||||
* data is invalidated and cache it.
|
||||
*
|
||||
* @param enabledElement - The Cornerstone's enabledElement.
|
||||
* @param svgDrawingHelper - The svgDrawingHelper providing the context for drawing.
|
||||
*/
|
||||
renderAnnotation = (
|
||||
enabledElement,
|
||||
svgDrawingHelper
|
||||
) => {
|
||||
console.log(drawing)
|
||||
let renderStatus = false;
|
||||
const { viewport } = enabledElement;
|
||||
const { element } = viewport;
|
||||
|
||||
let annotations = getAnnotations(this.getToolName(), element);
|
||||
|
||||
if (!annotations?.length) {
|
||||
return renderStatus;
|
||||
}
|
||||
|
||||
annotations = this.filterInteractableAnnotationsForElement(
|
||||
element,
|
||||
annotations
|
||||
);
|
||||
|
||||
if (!annotations?.length) {
|
||||
return renderStatus;
|
||||
}
|
||||
|
||||
const targetId = this.getTargetId(viewport);
|
||||
const renderingEngine = viewport.getRenderingEngine();
|
||||
|
||||
const styleSpecifier = {
|
||||
toolGroupId: this.toolGroupId,
|
||||
toolName: this.getToolName(),
|
||||
viewportId: enabledElement.viewport.id,
|
||||
};
|
||||
|
||||
for (let i = 0; i < annotations.length; i++) {
|
||||
const annotation = annotations[i];
|
||||
const { annotationUID, data } = annotation;
|
||||
const { points, activeHandleIndex } = data.handles;
|
||||
const canvasCoordinates = points.map((p) => viewport.worldToCanvas(p));
|
||||
|
||||
styleSpecifier.annotationUID = annotationUID;
|
||||
|
||||
const { color, lineWidth, lineDash } = this.getAnnotationStyle({
|
||||
annotation,
|
||||
styleSpecifier,
|
||||
});
|
||||
|
||||
const { viewPlaneNormal, viewUp } = viewport.getCamera();
|
||||
|
||||
// If cachedStats does not exist, or the unit is missing (as part of import/hydration etc.),
|
||||
// force to recalculate the stats from the points
|
||||
if (
|
||||
!data.cachedStats[targetId] ||
|
||||
data.cachedStats[targetId].areaUnit == null
|
||||
) {
|
||||
data.cachedStats[targetId] = {
|
||||
Modality: null,
|
||||
area: null,
|
||||
max: null,
|
||||
mean: null,
|
||||
stdDev: null,
|
||||
areaUnit: null,
|
||||
};
|
||||
|
||||
this._calculateCachedStats(
|
||||
annotation,
|
||||
viewPlaneNormal,
|
||||
viewUp,
|
||||
renderingEngine,
|
||||
enabledElement
|
||||
);
|
||||
} else if (annotation.invalidated) {
|
||||
this._throttledCalculateCachedStats(
|
||||
annotation,
|
||||
viewPlaneNormal,
|
||||
viewUp,
|
||||
renderingEngine,
|
||||
enabledElement
|
||||
);
|
||||
|
||||
// If the invalidated data is as a result of volumeViewport manipulation
|
||||
// of the tools, we need to invalidate the related stackViewports data if
|
||||
// they are not at the referencedImageId, so that
|
||||
// when scrolling to the related slice in which the tool were manipulated
|
||||
// we re-render the correct tool position. This is due to stackViewport
|
||||
// which doesn't have the full volume at each time, and we are only working
|
||||
// on one slice at a time.
|
||||
if (viewport instanceof VolumeViewport) {
|
||||
const { referencedImageId } = annotation.metadata;
|
||||
|
||||
// invalidate all the relevant stackViewports if they are not
|
||||
// at the referencedImageId
|
||||
for (const targetId in data.cachedStats) {
|
||||
if (targetId.startsWith('imageId')) {
|
||||
const viewports = renderingEngine.getStackViewports();
|
||||
|
||||
const invalidatedStack = viewports.find((vp) => {
|
||||
// The stack viewport that contains the imageId but is not
|
||||
// showing it currently
|
||||
const referencedImageURI =
|
||||
csUtils.imageIdToURI(referencedImageId);
|
||||
const hasImageURI = vp.hasImageURI(referencedImageURI);
|
||||
const currentImageURI = csUtils.imageIdToURI(
|
||||
vp.getCurrentImageId()
|
||||
);
|
||||
return hasImageURI && currentImageURI !== referencedImageURI;
|
||||
});
|
||||
|
||||
if (invalidatedStack) {
|
||||
delete data.cachedStats[targetId];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If rendering engine has been destroyed while rendering
|
||||
if (!viewport.getRenderingEngine()) {
|
||||
console.warn('Rendering Engine has been destroyed');
|
||||
return renderStatus;
|
||||
}
|
||||
|
||||
let activeHandleCanvasCoords;
|
||||
|
||||
if (!isAnnotationVisible(annotationUID)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (
|
||||
!isAnnotationLocked(annotationUID) &&
|
||||
!this.editData &&
|
||||
activeHandleIndex !== null && activeHandleIndex !== undefined
|
||||
) {
|
||||
// Not locked or creating and hovering over handle, so render handle.
|
||||
activeHandleCanvasCoords = [canvasCoordinates[activeHandleIndex]];
|
||||
}
|
||||
|
||||
if (activeHandleCanvasCoords) {
|
||||
const handleGroupUID = '0';
|
||||
|
||||
drawHandlesSvg(
|
||||
svgDrawingHelper,
|
||||
annotationUID,
|
||||
handleGroupUID,
|
||||
activeHandleCanvasCoords,
|
||||
{
|
||||
color,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
const dataId = `${annotationUID}-rect`;
|
||||
const rectangleUID = '0';
|
||||
drawRectSvg(
|
||||
svgDrawingHelper,
|
||||
annotationUID,
|
||||
rectangleUID,
|
||||
canvasCoordinates,
|
||||
{
|
||||
color,
|
||||
lineDash,
|
||||
lineWidth,
|
||||
},
|
||||
dataId
|
||||
);
|
||||
|
||||
renderStatus = true;
|
||||
|
||||
const options = this.getLinkedTextBoxStyle(styleSpecifier, annotation);
|
||||
if (!options.visibility) {
|
||||
data.handles.textBox = {
|
||||
hasMoved: false,
|
||||
worldPosition: [0, 0, 0],
|
||||
worldBoundingBox: {
|
||||
topLeft: [0, 0, 0],
|
||||
topRight: [0, 0, 0],
|
||||
bottomLeft: [0, 0, 0],
|
||||
bottomRight: [0, 0, 0],
|
||||
},
|
||||
};
|
||||
continue;
|
||||
}
|
||||
|
||||
const textLines = this.configuration.getTextLines(data, targetId);
|
||||
if (!textLines || textLines.length === 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!data.handles.textBox.hasMoved) {
|
||||
const canvasTextBoxCoords = getTextBoxCoordsCanvas(canvasCoordinates);
|
||||
|
||||
data.handles.textBox.worldPosition =
|
||||
viewport.canvasToWorld(canvasTextBoxCoords);
|
||||
}
|
||||
|
||||
const textBoxPosition = viewport.worldToCanvas(
|
||||
data.handles.textBox.worldPosition
|
||||
);
|
||||
|
||||
const textBoxUID = '1';
|
||||
const boundingBox = drawLinkedTextBoxSvg(
|
||||
svgDrawingHelper,
|
||||
annotationUID,
|
||||
textBoxUID,
|
||||
textLines,
|
||||
textBoxPosition,
|
||||
canvasCoordinates,
|
||||
{},
|
||||
options
|
||||
);
|
||||
|
||||
const { x: left, y: top, width, height } = boundingBox;
|
||||
|
||||
data.handles.textBox.worldBoundingBox = {
|
||||
topLeft: viewport.canvasToWorld([left, top]),
|
||||
topRight: viewport.canvasToWorld([left + width, top]),
|
||||
bottomLeft: viewport.canvasToWorld([left, top + height]),
|
||||
bottomRight: viewport.canvasToWorld([left + width, top + height]),
|
||||
};
|
||||
}
|
||||
|
||||
return renderStatus;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
|
||||
export default RectangleROITool;
|
Loading…
Reference in New Issue