From 383f45823c58149baf162a2de7bad2240ba0b030 Mon Sep 17 00:00:00 2001 From: Alireza Date: Mon, 5 May 2025 11:27:26 -0400 Subject: [PATCH] refactor(viewport): Remove resize detector and optimize viewport resizing logic (#5006) --- .../src/Viewport/OHIFCornerstoneViewport.tsx | 28 ++++++++++++------- extensions/cornerstone/src/init.tsx | 10 ------- .../CornerstoneViewportService.ts | 27 +++++++++++------- .../ViewportService/IViewportService.ts | 3 +- platform/app/src/components/ViewportGrid.tsx | 16 ++--------- 5 files changed, 38 insertions(+), 46 deletions(-) diff --git a/extensions/cornerstone/src/Viewport/OHIFCornerstoneViewport.tsx b/extensions/cornerstone/src/Viewport/OHIFCornerstoneViewport.tsx index 9f270afd4..d28bda89d 100644 --- a/extensions/cornerstone/src/Viewport/OHIFCornerstoneViewport.tsx +++ b/extensions/cornerstone/src/Viewport/OHIFCornerstoneViewport.tsx @@ -1,5 +1,4 @@ import React, { useEffect, useRef, useCallback, useState } from 'react'; -import { useResizeDetector } from 'react-resize-detector'; import * as cs3DTools from '@cornerstonejs/tools'; import { Enums, eventTarget, getEnabledElement } from '@cornerstonejs/core'; import { MeasurementService } from '@ohif/core'; @@ -110,7 +109,23 @@ const OHIFCornerstoneViewport = React.memo( cornerstoneViewportService.resize(); setImageScrollBarHeight(); } - }, [elementRef]); + }, [elementRef, cornerstoneViewportService, setImageScrollBarHeight]); + + useEffect(() => { + const element = elementRef.current; + if (!element) { + return; + } + + const resizeObserver = new ResizeObserver(onResize); + resizeObserver.observe(element); + + // Cleanup function + return () => { + resizeObserver.unobserve(element); + resizeObserver.disconnect(); + }; + }, [onResize]); const cleanUpServices = useCallback( viewportInfo => { @@ -350,10 +365,6 @@ const OHIFCornerstoneViewport = React.memo( } }, [displaySets, viewportId, viewportActionCornersService, servicesManager, commandsManager]); - const { ref: resizeRef } = useResizeDetector({ - onResize, - }); - const Notification = customizationService.getCustomization('ui.notificationComponent'); return ( @@ -364,10 +375,7 @@ const OHIFCornerstoneViewport = React.memo( style={{ height: '100%', width: '100%' }} onContextMenu={e => e.preventDefault()} onMouseDown={e => e.preventDefault()} - ref={el => { - resizeRef.current = el; - elementRef.current = el; - }} + ref={elementRef} > { - cornerstoneViewportService.resize(true); - }); - initContextMenu({ cornerstoneViewportService, customizationService, diff --git a/extensions/cornerstone/src/services/ViewportService/CornerstoneViewportService.ts b/extensions/cornerstone/src/services/ViewportService/CornerstoneViewportService.ts index 890ef4bf2..e05ce06f5 100644 --- a/extensions/cornerstone/src/services/ViewportService/CornerstoneViewportService.ts +++ b/extensions/cornerstone/src/services/ViewportService/CornerstoneViewportService.ts @@ -38,6 +38,9 @@ const EVENTS = { VIEWPORT_VOLUMES_CHANGED: 'event::cornerstoneViewportService:viewportVolumesChanged', }; +const MIN_STACK_VIEWPORTS_TO_ENQUEUE_RESIZE = 12; +const MIN_VOLUME_VIEWPORTS_TO_ENQUEUE_RESIZE = 6; + export const WITH_NAVIGATION = { withNavigation: true, withOrientation: false }; /** @@ -62,9 +65,6 @@ class CornerstoneViewportService extends PubSubService implements IViewportServi beforeResizePositionPresentations: Map = new Map(); // Some configs - enableResizeDetector: true; - resizeRefreshRateMs: 200; - resizeRefreshMode: 'debounce'; servicesManager: AppTypes.ServicesManager = null; resizeQueue = []; @@ -125,13 +125,8 @@ class CornerstoneViewportService extends PubSubService implements IViewportServi /** * It triggers the resize on the rendering engine, and renders the viewports * - * @param isGridResize - if the resize is triggered by a grid resize - * this is used to avoid double resize of the viewports since if the - * grid is resized, all viewports will be resized so there is no need - * to resize them individually which will get triggered by their - * individual resize observers */ - public resize(isGridResize = false) { + public resize() { // https://stackoverflow.com/a/26279685 // This resize() call, among other things, rerenders the viewports. But when the entire viewer is // display: none'd, it makes the size of all hidden elements 0, including the viewport canvas and its containers. @@ -147,12 +142,24 @@ class CornerstoneViewportService extends PubSubService implements IViewportServi return; } + const numStackViewportsInViewportGrid = Array.from(this.viewportsById.values()).filter( + viewportInfo => viewportInfo.getViewportType() === csEnums.ViewportType.STACK + ).length; + + const numVolumeViewportsInViewportGrid = Array.from(this.viewportsById.values()).filter( + viewportInfo => viewportInfo.getViewportType() === csEnums.ViewportType.ORTHOGRAPHIC + ).length; + + const isEasyResize = + numStackViewportsInViewportGrid <= MIN_STACK_VIEWPORTS_TO_ENQUEUE_RESIZE && + numVolumeViewportsInViewportGrid <= MIN_VOLUME_VIEWPORTS_TO_ENQUEUE_RESIZE; + // if there is a grid resize happening, it means the viewport grid // has been manipulated (e.g., panels closed, added, etc.) and we need // to resize all viewports, so we will add a timeout here to make sure // we don't double resize the viewports when viewports in the grid are // resized individually - if (isGridResize) { + if (isEasyResize) { this.performResize(); this.resetGridResizeTimeout(); this.resizeQueue = []; diff --git a/extensions/cornerstone/src/services/ViewportService/IViewportService.ts b/extensions/cornerstone/src/services/ViewportService/IViewportService.ts index 46d4acd5b..fbf0f47eb 100644 --- a/extensions/cornerstone/src/services/ViewportService/IViewportService.ts +++ b/extensions/cornerstone/src/services/ViewportService/IViewportService.ts @@ -35,9 +35,8 @@ export interface IViewportService { /** * It creates a resize observer for the viewport element, and observes * the element for resizing events - * @param {*} elementRef */ - resize(isGridResize: boolean): void; + resize(): void; /** * Removes the viewport from cornerstone, and destroys the rendering engine */ diff --git a/platform/app/src/components/ViewportGrid.tsx b/platform/app/src/components/ViewportGrid.tsx index ba5f5e1ce..001a2ce46 100644 --- a/platform/app/src/components/ViewportGrid.tsx +++ b/platform/app/src/components/ViewportGrid.tsx @@ -1,5 +1,4 @@ -import React, { useEffect, useCallback, useRef, useMemo } from 'react'; -import { useResizeDetector } from 'react-resize-detector'; +import React, { useEffect, useCallback, useRef } from 'react'; import { Types, MeasurementService } from '@ohif/core'; import { ViewportGrid, ViewportPane } from '@ohif/ui-next'; import { useViewportGrid } from '@ohif/ui-next'; @@ -13,14 +12,6 @@ function ViewerViewportGrid(props: withAppTypes) { const { layout, activeViewportId, viewports, isHangingProtocolLayout } = viewportGrid; const { numCols, numRows } = layout; - const { ref: resizeRef } = useResizeDetector({ - refreshMode: 'debounce', - refreshRate: 7, - refreshOptions: { leading: true }, - onResize: () => { - viewportGridService.setViewportGridSizeChanged(); - }, - }); const layoutHash = useRef(null); const { @@ -444,10 +435,7 @@ function ViewerViewportGrid(props: withAppTypes) { } return ( -
+