How to draw a rectangle on the viewport given coordinates?

I want to be able to plot a rectangle like the Rectangle ROI tool does by having a function with parameters that specify coordinates. How can I do this

Hi, I had the same desire and had quite a bit of struggle to achieve it. Being new to OHIF, I’m not sure if my approach is the right one, but I finally created a new tool that can display rectangles as overlays on StackViewports.

To understand how this works, it helped me a lot to look at extensions/cornerstone/src/tools/ImageOverlayViewerTool.tsx. If you add something like the following code, you can plot rectangles on the current viewport and toggle the visibility with the ImageOverlay Button.

private  _renderRectangles(enabledElement, svgDrawingHelper, metaData ) {
    const { viewport } = enabledElement;
    const imageId = this.getReferencedImageId(viewport);
    const svgns = 'http://www.w3.org/2000/svg';
    const x = metaData.x; // center of rectangle x-coord
    const y = metaData.y; // center of rectangle y-coord
    const width = metaData.width; // width of rectangle
    const height = metaData.height; // height of rectangle
    const color = metaData.color; // color of the rectangle

    // Calculate world and canvas coordinates for the rectangle
    const overlayTopLeftWorldPos = utilities.imageToWorldCoords(imageId, [x - width / 2, y - height / 2]);
    const overlayTopLeftOnCanvas = viewport.worldToCanvas(overlayTopLeftWorldPos);
    const overlayBottomRightWorldPos = utilities.imageToWorldCoords(imageId, [x + width / 2, y + height / 2]);
    const overlayBottomRightOnCanvas = viewport.worldToCanvas(overlayBottomRightWorldPos);

    // Define rectangle attributes
    const rectId = `rectangle-overlay-${metaData.id}`;


    const rectAttributes = {
        'data-id': rectId,
        width: overlayBottomRightOnCanvas[0] - overlayTopLeftOnCanvas[0],
        height: overlayBottomRightOnCanvas[1] - overlayTopLeftOnCanvas[1],
        x: overlayTopLeftOnCanvas[0],
        y: overlayTopLeftOnCanvas[1],
        fill: `rgba(${color.join(',')})`, // Convert color array to CSS rgba string
        'stroke-width': 2,
        stroke: 'black',
    };

    // Create or update the rectangle element in the SVG
    const rectElement = svgDrawingHelper.getSvgNode(rectId);
    if (rectElement) {
        drawing.setAttributesIfNecessary(rectAttributes, rectElement);
        svgDrawingHelper.setNodeTouched(rectId);
    } else {
        const rectElement = document.createElementNS(svgns, 'rect');
        drawing.setNewAttributesIfValid(rectAttributes, rectElement);
        svgDrawingHelper.appendNode(rectElement, rectId);
    }
}
1 Like

Thanks so much! I was attempting to parse through the library but its so chaotic. After adding this function to extensions/cornerstone/src/tools/ImageOverlayViewerTool.tsx would I simply be able to import the function anywhere else in the directory? I created a button located in modes/longitudinal/src/ that runs a command in extensions/cornerstone/src/commandsModule.ts when pressed. How can I run it since enabledElement and svgDrawingHelper are local variables to extensions/cornerstone/src/tools/ImageOverlayViewerTool.tsx, right?

Hey, just following up

Hey, I don’t think you can just call this function from somewhere else, I think you are supposed to do the detour over the Tools. Let’s say you run this function inside …/ImageOverlayViewerTool.tsx :

this._renderRectangles(enabledElement, svgDrawingHelper, rectMetadata);

Then, when you toggle the ImageOverlay Button, the Rectangle is shown/not shown. From there I continued by creating a separate tool that is specifically for Rectangles, doing it exactly as they created the ImageOverlayViewerTool.

So you made a new button? That’s essentially what I want to do as well. I am trying to add AI bounding box capabilities through a button. So far, I have a button that sends a request to a backend and gets coordinates for a bounding box. Can you elaborate on how you used the code provided in your first post and how you incorporated it into a button? Did you just copy paste the code from ImageOverlayViewerTool.tsx to make a new tool and modify it to only do rectangles? Thanks so much.

Did you just copy paste the code from ImageOverlayViewerTool.tsx to make a new tool and modify it to only do rectangles?

Basically yes, I followed the steps they did in this Commit: feat(ImageOverlayViewerTool): add ImageOverlayViewer tool that can re… · OHIF/Viewers@69115da · GitHub

So I did:

  1. copy code from ImageOverlayViewerTool.tsx
  2. add new tool to Tool Group
  3. create Button