How to Integrate the cornerstone3d Examples in a react app

960 Views Asked by At

I'm trying to implement the examples in https://www.cornerstonejs.org/docs/examples#run-examples-locally in a react app but I'm not sure what to do.

What I did:

  • npx create-react-app my-app --template typescript
  • npm install
  • npm install @cornerstonejs/core @cornerstonejs/tools @cornerstonejs/streaming-image-volume-loader
  • moved the util and packages from corerstone3d.beta folders into the project src folder
  • Imported the libraries via the code on the bottom of this page
  • Copied some of the html into the App() { return (.......); }
  • Copied some of the code from here:" https://www.cornerstonejs.org/live-examples/stackbasic " (check console for source code) into App.tsx
  • Fixed some of the errors via importing missing libraries

I get however thousands of errors from the functions in util and packages folders that i took from cornerstone3d as seen in the next picture: enter image description here

import React from 'react';
import logo from './logo.svg';
import './App.css';
import { RenderingEngine, Types, Enums } from '@cornerstonejs/core';
import {
  initDemo,
  createImageIdsAndCacheMetaData,
  setTitleAndDescription,
  ctVoiRange,
} from './utils/demo/helpers';


function App() {
  
const { ViewportType } = Enums;
// ======== Set up page ======== //
setTitleAndDescription(
  'Basic Stack',
  'Displays a single DICOM image in a Stack viewport.'
);

const content = document.getElementById('content');
const element = document.createElement('div');
element.id = 'cornerstone-element';
element.style.width = '500px';
element.style.height = '500px';

// ============================= //
  return (

    <html>
  <head>
    <meta http-equiv="Content-type" content="text/html; charset=utf-8" />
    <meta
      name="viewport"
      content="width=device-width, height=device-height, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no"
    />
  <script defer src="stackBasic.js"></script></head>
  <body>
    Style the title and description components so they are the same in every demo
    <div id="demo-title-container">
      <h1 id="demo-title">
        Insert demo title here during demo 
      </h1>
    </div>
    <div id="demo-description-container">
      <p id="demo-description">
       Insert demo title here during demo
      </p>
    </div>
    <div id="demo-toolbar">
      Insert buttons/dropdowns/etc here during demo
    </div>
    <div id="content"></div>
  </body>
</html>

  );
}

export default App;

(the code is missing some parts as im trying to fix the import first)

If any solutions on other frameworks exist I would also like to have a look at them

2

There are 2 best solutions below

1
Marco Sousa On

you need to download cornerstone3d at cornerstone3d again and then install all node packages with npm install

0
syed hussain haider zaidi On

If you are still seeking this answer, here is the implementation I applied.

import React, { useEffect, useRef, useState } from "react"
import {
  RenderingEngine,
  Types,
  Enums,
  cache,
  eventTarget,
  getRenderingEngine,
} from "@cornerstonejs/core"
import * as cornerstoneTools from "@cornerstonejs/tools"
import createImageIdsAndCacheMetaData from "./helper/createImageIdsAndCacheMetaData"
import s from "./Cornerstone.module.scss"
import { Instance } from "../types"
import { useDispatch } from "../context"
import { wadoURL } from "./constants"
import { isSingleImageAndStack } from "../../../utils"
import "./initialization"

const {
  StackScrollMouseWheelTool,
  WindowLevelTool,
  ZoomTool,
  ToolGroupManager,
  Enums: csToolsEnums,
} = cornerstoneTools

const { ViewportType, Events } = Enums
const { MouseBindings, KeyboardBindings } = csToolsEnums

cornerstoneTools.addTool(StackScrollMouseWheelTool)
cornerstoneTools.addTool(WindowLevelTool)
cornerstoneTools.addTool(ZoomTool)

const toolGroup = ToolGroupManager.createToolGroup("toolGroupId")
toolGroup?.addTool(StackScrollMouseWheelTool.toolName)
toolGroup?.addTool(WindowLevelTool.toolName)
toolGroup?.addTool(ZoomTool.toolName)

toolGroup?.setToolActive(StackScrollMouseWheelTool.toolName)
toolGroup?.setToolActive(WindowLevelTool.toolName, {
  bindings: [
    {
      mouseButton: MouseBindings.Primary, // Left Click
    },
  ],
})
toolGroup?.setToolActive(ZoomTool.toolName, {
  bindings: [
    {
      mouseButton: MouseBindings.Primary, // Ctrl + Left Click
      modifierKey: KeyboardBindings.Ctrl,
    },
  ],
})

interface Instance {
  contentType: "dicom"
  studyId: string
  seriesId: string
  instanceId: string
  frames: number
  modality: string
}

interface OwnProps {
  instance: Instance
}

const renderingEngineId = "myRenderingEngine"
let renderingEngine: Types.IRenderingEngine | undefined

const Cornerstone = ({ instance }: OwnProps) => {
  const [loading, setLoading] = useState(true)
  const imageIdsCount = useRef<number | null>(null)
  const imageIdsLoaded = useRef<number | null>(null)
  const prevPropsRef = useRef<Instance | null>(null)
  const viewportRef = useRef<HTMLDivElement | null>(null)
  const { state } = useDispatch()

  const isSingleImage = isSingleImageAndStack(instance)

  useEffect(() => {
    window.addEventListener("resize", handleResize)
    return () => {
      // renderingEngine?.destroy()
      window.removeEventListener("resize", handleResize)
    }
  }, [])

  useEffect(() => {
    if (renderingEngine) {
      renderingEngine?.resize(true, false)
    }
  }, [state.layout])

  useEffect(() => {
    // Compare the new props with the old props
    if (instance !== prevPropsRef.current) {
      const viewportInput: Types.PublicViewportInput = {
        viewportId: instance.instanceId,
        type: ViewportType.STACK,
        element: viewportRef.current as HTMLDivElement,
        defaultOptions: {
          background: [0, 0, 0],
        },
      }

      renderingEngine = getRenderingEngine(renderingEngineId)

      if (!renderingEngine || renderingEngine.hasBeenDestroyed) {
        // Instantiate a rendering engine
        renderingEngine = new RenderingEngine(renderingEngineId)
      }

      if (prevPropsRef.current)
        renderingEngine.disableElement(prevPropsRef.current.instanceId)
      renderingEngine.enableElement(viewportInput)

      const baseImageURI = `${isSingleImage ? "web" : "wadors"}:${wadoURL}/studies/${instance.studyId}/series/${instance.seriesId}/instances/${instance.instanceId}/frames/1`;

      const imageURIs = [
        `${baseImageURI}/rendered`,
        baseImageURI
      ];
      
      let isLoaded;

      for (const imageURI of imageURIs) {
        const cachedImage = cache.getCachedImageBasedOnImageURI(imageURI);
      
        if (cachedImage !== undefined) {
          isLoaded = cachedImage.loaded;
          break; // Exit the loop if a valid loaded value is found
        }
      }

      if (!isLoaded) setLoading(true)
      else setLoading(false)
      render()
    }
    imageIdsLoaded.current = 0
    // Update the previous props ref
    prevPropsRef.current = instance
    // eslint-disable-next-line
  }, [instance])

  eventTarget?.addEventListener(Events.IMAGE_LOADED, (e: unknown) => {
    imageIdsLoaded.current = (imageIdsLoaded?.current || 0) + 1
    if (
      imageIdsCount.current !== null &&
      imageIdsLoaded.current >= imageIdsCount.current
    )
      setLoading(false)
  })

  const handleResize = () => {
    if (renderingEngine) {
      renderingEngine?.resize(true, false)
    }
  }

  const render = async () => {
    try {
      const viewportId = instance.instanceId

      let imageIds = [
        `web:${wadoURL}/studies/${instance.studyId}/series/${instance.seriesId}/instances/${viewportId}/frames/1/rendered`,
      ]

      // Get Cornerstone imageIds and fetch metadata into RAM
      imageIds = await createImageIdsAndCacheMetaData({
        StudyInstanceUID: instance.studyId,
        SeriesInstanceUID: instance.seriesId,
        SOPInstanceUID: isSingleImage ? viewportId : null,
        wadoRsRoot: wadoURL,
        isSingleImage,
      })

      imageIdsCount.current = imageIds.length

      toolGroup?.addViewport(viewportId, renderingEngineId)

      // get renderingEngine from cache if it exists
      renderingEngine = getRenderingEngine(renderingEngineId)

      let viewport = renderingEngine?.getViewport(viewportId) as
        | Types.IVolumeViewport
        | Types.IStackViewport

      const stackViewport = viewport as Types.IStackViewport
      if (!isSingleImage) {
        imageIds.forEach((imageId, index) => {
          stackViewport.setStack(imageIds, index)
        })
      }
      stackViewport.setStack(imageIds)
      viewport = stackViewport
      // Render the image
      // viewport?.render()
      renderingEngine?.renderViewports([viewportId])
    } catch (err) {
      console.log(err)
    }
  }

  return (
    <div ref={viewportRef} style={{ width: "100%", height: "100%" }}>
      {loading ? <p className={s.loading}>Loading...</p> : null}
    </div>
  )
}

export default Cornerstone
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>