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

What should I do with toggleEnabledDisabledToolbar? And where do I run

this._renderRectangles(enabledElement, svgDrawingHelper, rectMetadata);
(I duplicated ImageOverlayViewerTool.tsx and added the code you provided earlier but Iā€™m still unclear on how the code works and where I should insert this code to call it)

If you have one, do you mind sharing your repo?
Thanks so much for your help.

If you add the new Tool to a ToolGroup you can ā€œrunā€ it when toggling the button.

My code is public: https://github.com/TomWartm/Viewers/blob/efb2c80d569c6fc4e612ebfa8ffb557667e41600/extensions/text-input-extension/src/tools/RectangleOverlayViewerTool.tsx

I hope this helps.

Thanks so much for your help, really appreciate it!
Currently, I am calling a function Iā€™ve written from commandsModule.ts that jumps to a specific slice of a CT scan. Can I make the rectangle overlay work while still doing this (I want to put it inside the function)?

Also, how do I specify the parameters for the rectangle overlay? Thatā€™s still unclear to me.

Good question. I also donā€™t know where the two elements: enabledElement and svgDrawingHelper are defined and why they can be used without an import.

It would be great if someone else could help with this, as this would help a lot to understand how these Tools work.

Take a look at out dynamic annotation addition https://www.cornerstonejs.org/live-examples/dynamicallyaddannotations

1 Like

How do we implement this and do rectangles?
I found the code for the link you gave me but still unclear how to do for rectangles. cornerstone3D/packages/tools/examples/dynamicallyAddAnnotations/index.ts at 21f9869a43912ca4a1eb70391d439e9e11252f07 Ā· cornerstonejs/cornerstone3D (github.com)

Basically what I want to do is press a button in the toolbar, which gets the current dicom data and sends it to the backend, which runs a model and sends the results back (async). Then, I plot a rectangle on the viewport given the output.

2 Likes

@alireza I have added the same code to add dynamic measurements, but thatā€™s not working for me. Please help.

@alireza Can I simply run cs3dTools.annotation.state.addAnnotation(annotationData); within a function activated by a button? Can I define the annotation (rectangle coordinates, etc.) within my code instead of fetching it from an existing one? How would I do this?

Thanks!

Yes, take a look at how you can assign commands to buttons to run in the toolbar

and

Thanks! My other question was how to define the annotation? In the faq you use a pre-existing annotation, but what if I want to create one with my own parameters?

What are your parameters for an instance? I assume you need something like image coordinates or world coordinates at the very least.

Yeah just image coordinates for where to put the rectangle, thatā€™s it.