feat(resize): Optimize resizing process and maintain zoom level (#3889)

This commit is contained in:
Alireza authored and GitHub committed 2024-02-21 13:49:23 -05:00
1 parent a2a0090eeb
commit b3a0faf5f5
37 files changed
+1395 -968

No files matched your search

@@ -24,6 +24,8 @@
"yarn": ">=1.18.0"
},
"scripts": {
"clean": "shx rm -rf dist",
"clean:deep": "yarn run clean && shx rm -rf node_modules",
"dev": "cross-env NODE_ENV=development webpack --config .webpack/webpack.dev.js --watch --output-pathinfo",
"dev:dicom-seg": "yarn run dev",
"build": "cross-env NODE_ENV=production webpack --config .webpack/webpack.prod.js",
@@ -24,6 +24,8 @@
"yarn": ">=1.18.0"
},
"scripts": {
"clean": "shx rm -rf dist",
"clean:deep": "yarn run clean && shx rm -rf node_modules",
"dev": "cross-env NODE_ENV=development webpack --config .webpack/webpack.dev.js --watch --output-pathinfo",
"dev:dicom-seg": "yarn run dev",
"build": "cross-env NODE_ENV=production webpack --config .webpack/webpack.prod.js",
@@ -116,7 +116,6 @@ function OHIFCornerstoneSEGViewport(props) {
}}
onElementEnabled={onElementEnabled}
onElementDisabled={onElementDisabled}
// initialImageIndex={initialImageIndex}
></Component>
);
}, [viewportId, segDisplaySet, toolGroupId]);
@@ -23,6 +23,8 @@
"ohif-extension"
],
"scripts": {
"clean": "shx rm -rf dist",
"clean:deep": "yarn run clean && shx rm -rf node_modules",
"dev": "cross-env NODE_ENV=development webpack --config .webpack/webpack.dev.js --watch --output-pathinfo",
"dev:cornerstone": "yarn run dev",
"build": "cross-env NODE_ENV=production webpack --config .webpack/webpack.prod.js",
+2
View File
@@ -25,6 +25,8 @@
"access": "public"
},
"scripts": {
"clean": "shx rm -rf dist",
"clean:deep": "yarn run clean && shx rm -rf node_modules",
"dev": "cross-env NODE_ENV=development webpack --config .webpack/webpack.dev.js --watch --output-pathinfo",
"dev:cornerstone": "yarn run dev",
"build": "cross-env NODE_ENV=production webpack --progress --config .webpack/webpack.prod.js",
@@ -21,6 +21,7 @@ import getSOPInstanceAttributes from '../utils/measurementServiceMappings/utils/
import CornerstoneServices from '../types/CornerstoneServices';
import CinePlayer from '../components/CinePlayer';
import { Types } from '@ohif/core';
import { LutPresentation, PositionPresentation } from '../types/Presentation';
const STACK = 'stack';
@@ -101,7 +102,6 @@ const OHIFCornerstoneViewport = React.memo(props => {
viewportOptions,
displaySetOptions,
servicesManager,
commandsManager,
onElementEnabled,
onElementDisabled,
isJumpToMeasurementDisabled,
@@ -277,7 +277,10 @@ const OHIFCornerstoneViewport = React.memo(props => {
// The presentation state will have been stored previously by closing
// a viewport. Otherwise, this viewport will be unchanged and the
// presentation information will be directly carried over.
const { lutPresentationStore, positionPresentationStore } = stateSyncService.getState();
const state = stateSyncService.getState();
const lutPresentationStore = state.lutPresentationStore as LutPresentation;
const positionPresentationStore = state.positionPresentationStore as PositionPresentation;
const { presentationIds } = viewportOptions;
const presentations = {
positionPresentation: positionPresentationStore[presentationIds?.positionPresentationId],
@@ -360,8 +363,6 @@ const OHIFCornerstoneViewport = React.memo(props => {
<React.Fragment>
<div className="viewport-wrapper">
<ReactResizeDetector
refreshMode="debounce"
refreshRate={50} // Wait 50 ms after last move to render
onResize={onResize}
targetRef={elementRef.current}
/>
+10
View File
@@ -204,6 +204,16 @@ export default async function init({
}
);
// resize the cornerstone viewport service when the grid size changes
// IMPORTANT: this should happen outside of the OHIFCornerstoneViewport
// since it will trigger a rerender of each viewport and each resizing
// the offscreen canvas which would result in a performance hit, this should
// done only once per grid resize here. Doing it once here, allows us to reduce
// the refreshRage(in ms) to 10 from 50. I tried with even 1 or 5 ms it worked fine
viewportGridService.subscribe(viewportGridService.EVENTS.GRID_SIZE_CHANGED, () => {
cornerstoneViewportService.resize(true);
});
initContextMenu({
cornerstoneViewportService,
customizationService,
@@ -67,4 +67,4 @@ type SegmentationRepresentationData = {
LABELMAP?: LabelmapSegmentationData;
};
export { SegmentationConfig, Segment, Segmentation };
export type { SegmentationConfig, Segment, Segmentation };
@@ -10,6 +10,7 @@ import {
VolumeViewport3D,
cache,
Enums as csEnums,
BaseVolumeViewport,
} from '@cornerstonejs/core';
import { utilities as csToolsUtils, Enums as csToolsEnums } from '@cornerstonejs/tools';
@@ -17,7 +18,7 @@ import { IViewportService } from './IViewportService';
import { RENDERING_ENGINE_ID } from './constants';
import ViewportInfo, { DisplaySetOptions, PublicViewportOptions } from './Viewport';
import { StackViewportData, VolumeViewportData } from '../../types/CornerstoneCacheService';
import { Presentation, Presentations } from '../../types/Presentation';
import { LutPresentation, PositionPresentation, Presentations } from '../../types/Presentation';
import JumpPresets from '../../utils/JumpPresets';
@@ -44,6 +45,7 @@ class CornerstoneViewportService extends PubSubService implements IViewportServi
viewportsById: Map<string, ViewportInfo> = new Map();
viewportGridResizeObserver: ResizeObserver | null;
viewportsDisplaySets: Map<string, string[]> = new Map();
beforeResizePositionPresentations: Map<string, PositionPresentation> = new Map();
// Some configs
enableResizeDetector: true;
@@ -51,6 +53,11 @@ class CornerstoneViewportService extends PubSubService implements IViewportServi
resizeRefreshMode: 'debounce';
servicesManager = null;
resizeQueue = [];
viewportResizeTimer = null;
gridResizeDelay = 50;
gridResizeTimeOut = null;
constructor(servicesManager: ServicesManager) {
super(EVENTS);
this.renderingEngine = null;
@@ -94,14 +101,28 @@ class CornerstoneViewportService extends PubSubService implements IViewportServi
}
/**
* It triggers the resize on the rendering engine.
* 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() {
const immediate = true;
const keepCamera = true;
this.renderingEngine.resize(immediate, keepCamera);
this.renderingEngine.render();
public resize(isGridResize = false) {
// 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) {
this.performResize();
this.resetGridResizeTimeout();
this.resizeQueue = [];
clearTimeout(this.viewportResizeTimer);
} else {
this.enqueueViewportResizeRequest();
}
}
/**
@@ -138,97 +159,239 @@ class CornerstoneViewportService extends PubSubService implements IViewportServi
this.viewportsDisplaySets.delete(viewportId);
}
public setPresentations(viewport, presentations?: Presentations): void {
const properties = presentations?.lutPresentation?.properties;
if (properties) {
viewport.setProperties(properties);
/**
* Sets the presentations for a given viewport. Presentations is an object
* that can define the lut or position for a viewport.
*
* @param viewportId - The ID of the viewport.
* @param presentations - The presentations to apply to the viewport.
*/
public setPresentations(viewportId: string, presentations?: Presentations): void {
const viewport = this.getCornerstoneViewport(viewportId) as
| Types.IStackViewport
| Types.IVolumeViewport;
if (!viewport) {
return;
}
const camera = presentations?.positionPresentation?.camera;
if (camera) {
viewport.setCamera(camera);
if (!presentations) {
return;
}
const { lutPresentation, positionPresentation } = presentations;
if (lutPresentation) {
const { presentation } = lutPresentation;
if (viewport instanceof BaseVolumeViewport) {
Object.entries(presentation).forEach(
([volumeId, properties]: [string, Types.ViewportProperties]) => {
viewport.setProperties(properties, volumeId);
}
);
} else {
viewport.setProperties(presentation);
}
}
if (positionPresentation) {
const { viewPlaneNormal, viewUp, zoom, pan } = positionPresentation.presentation;
viewport.setCamera({ viewPlaneNormal, viewUp });
if (zoom !== undefined) {
viewport.setZoom(zoom);
}
if (pan !== undefined) {
viewport.setPan(pan);
}
}
}
public getPresentation(viewportId: string): Presentation {
/**
* Retrieves the position presentation information for a given viewport.
* @param viewportId The ID of the viewport.
* @returns The position presentation object containing various properties
* such as ID, viewport type, initial image index, view plane normal, view up, zoom, and pan.
*/
public getPositionPresentation(viewportId: string): PositionPresentation {
const viewportInfo = this.viewportsById.get(viewportId);
if (!viewportInfo) {
return;
}
const { viewportType, presentationIds } = viewportInfo.getViewportOptions();
const presentationIds = viewportInfo.getPresentationIds();
if (!presentationIds) {
return;
}
const { positionPresentationId } = presentationIds;
const csViewport = this.getCornerstoneViewport(viewportId);
if (!csViewport) {
return;
}
const properties = csViewport.getProperties();
if (properties.isComputedVOI) {
delete properties.voiRange;
delete properties.VOILUTFunction;
}
const { viewPlaneNormal, viewUp } = csViewport.getCamera();
const initialImageIndex = csViewport.getCurrentImageIdIndex();
const camera = csViewport.getCamera();
const zoom = csViewport.getZoom();
const pan = csViewport.getPan();
return {
presentationIds,
viewportType: !viewportType || viewportType === 'stack' ? 'stack' : 'volume',
properties,
initialImageIndex,
camera,
id: positionPresentationId,
viewportType: viewportInfo.getViewportType(),
presentation: {
initialImageIndex,
viewUp,
viewPlaneNormal,
zoom,
pan,
},
};
}
public storePresentation({ viewportId }) {
const { stateSyncService, syncGroupService } = this.servicesManager.services;
let presentation;
try {
presentation = this.getPresentation(viewportId);
} catch (error) {
console.warn(error);
}
if (!presentation || !presentation.presentationIds) {
/**
* Retrieves the LUT (Lookup Table) presentation for a given viewport.
* @param viewportId The ID of the viewport.
* @returns The LUT presentation object, or undefined if the viewport does not exist.
*/
public getLutPresentation(viewportId: string): LutPresentation {
const viewportInfo = this.viewportsById.get(viewportId);
if (!viewportInfo) {
return;
}
const presentationIds = viewportInfo.getPresentationIds();
if (!presentationIds) {
return;
}
const { lutPresentationId } = presentationIds;
const csViewport = this.getCornerstoneViewport(viewportId) as
| Types.IStackViewport
| Types.IVolumeViewport;
if (!csViewport) {
return;
}
const cleanProperties = properties => {
if (properties.isComputedVOI) {
delete properties.voiRange;
delete properties.VOILUTFunction;
}
return properties;
};
const presentation =
csViewport instanceof BaseVolumeViewport
? new Map()
: cleanProperties(csViewport.getProperties());
if (presentation instanceof Map) {
csViewport.getActors().forEach(({ uid: volumeId }) => {
const properties = cleanProperties(csViewport.getProperties(volumeId));
presentation.set(volumeId, properties);
});
}
return {
id: lutPresentationId,
viewportType: viewportInfo.getViewportType(),
presentation,
};
}
/**
* Retrieves the presentations for a given viewport.
* @param viewportId - The ID of the viewport.
* @returns The presentations for the viewport.
*/
public getPresentations(viewportId: string): Presentations {
const viewportInfo = this.viewportsById.get(viewportId);
if (!viewportInfo) {
return;
}
const positionPresentation = this.getPositionPresentation(viewportId);
const lutPresentation = this.getLutPresentation(viewportId);
return {
positionPresentation,
lutPresentation,
};
}
/**
* Stores the presentation state for a given viewport inside the
* stateSyncService. This is used to persist the presentation state
* across different scenarios e.g., when the viewport is changing the
* display set, or when the viewport is moving to a different layout.
*
* @param viewportId The ID of the viewport.
*/
public storePresentation({ viewportId }) {
let presentations = null as Presentations;
try {
presentations = this.getPresentations(viewportId);
if (!presentations?.positionPresentation && !presentations?.lutPresentation) {
return;
}
} catch (error) {
console.warn(error);
return;
}
const { stateSyncService, syncGroupService } = this.servicesManager.services;
const synchronizers = syncGroupService.getSynchronizersForViewport(
viewportId,
this.renderingEngine.id
);
const { lutPresentationStore, positionPresentationStore, synchronizersStore } =
const { positionPresentationStore, synchronizersStore, lutPresentationStore } =
stateSyncService.getState();
const { presentationIds } = presentation;
const { lutPresentationId, positionPresentationId } = presentationIds || {};
const storeState = {};
const { lutPresentation, positionPresentation } = presentations;
const { id: positionPresentationId } = positionPresentation;
const { id: lutPresentationId } = lutPresentation;
const updateStore = (store, id, value) => ({ ...store, [id]: value });
const newState = {} as { [key: string]: any };
if (lutPresentationId) {
storeState.lutPresentationStore = {
...lutPresentationStore,
[lutPresentationId]: presentation,
};
newState.lutPresentationStore = updateStore(
lutPresentationStore,
lutPresentationId,
lutPresentation
);
}
if (positionPresentationId) {
storeState.positionPresentationStore = {
...positionPresentationStore,
[positionPresentationId]: presentation,
};
newState.positionPresentationStore = updateStore(
positionPresentationStore,
positionPresentationId,
positionPresentation
);
}
if (synchronizers?.length) {
storeState.synchronizersStore = {
...synchronizersStore,
[viewportId]: synchronizers.map(synchronizer => {
const sourceViewports = synchronizer.getSourceViewports();
const targetViewports = synchronizer.getTargetViewports();
return {
id: synchronizer.id,
sourceViewports: [...sourceViewports],
targetViewports: [...targetViewports],
};
}),
};
newState.synchronizersStore = updateStore(
synchronizersStore,
viewportId,
synchronizers.map(synchronizer => ({
id: synchronizer.id,
sourceViewports: [...synchronizer.getSourceViewports()],
targetViewports: [...synchronizer.getTargetViewports()],
}))
);
}
stateSyncService.store(storeState);
stateSyncService.store(newState);
}
/**
@@ -324,9 +487,13 @@ class CornerstoneViewportService extends PubSubService implements IViewportServi
});
}
public getCornerstoneViewport(
viewportId: string
): Types.IStackViewport | Types.IVolumeViewport | null {
/**
* Retrieves the Cornerstone viewport with the specified ID.
*
* @param viewportId - The ID of the viewport.
* @returns The Cornerstone viewport object if found, otherwise null.
*/
public getCornerstoneViewport(viewportId: string): Types.IViewport | null {
const viewportInfo = this.getViewportInfo(viewportId);
if (!viewportInfo || !this.renderingEngine || this.renderingEngine.hasBeenDestroyed) {
@@ -338,15 +505,51 @@ class CornerstoneViewportService extends PubSubService implements IViewportServi
return viewport;
}
/**
* Retrieves the viewport information for a given viewport ID. The viewport information
* is the OHIF construct that holds different options and data for a given viewport and
* is different from the cornerstone viewport.
*
* @param viewportId The ID of the viewport.
* @returns The viewport information.
*/
public getViewportInfo(viewportId: string): ViewportInfo {
return this.viewportsById.get(viewportId);
}
/**
* Looks through the viewports to see if the specified measurement can be
* displayed in one of the viewports.
*
* @param measurement
* The measurement that is desired to view.
* @param activeViewportId - the index that was active at the time the jump
* was initiated.
* @return the viewportId that the measurement should be displayed in.
*/
public getViewportIdToJump(
activeViewportId: string,
displaySetInstanceUID: string,
cameraProps: unknown
): string {
const viewportInfo = this.getViewportInfo(activeViewportId);
const { referencedImageId } = cameraProps;
if (viewportInfo?.contains(displaySetInstanceUID, referencedImageId)) {
return activeViewportId;
}
return (
[...this.viewportsById.values()].find(viewportInfo =>
viewportInfo.contains(displaySetInstanceUID, referencedImageId)
)?.viewportId ?? null
);
}
private async _setStackViewport(
viewport: Types.IStackViewport,
viewportData: StackViewportData,
viewportInfo: ViewportInfo,
presentations: Presentations
presentations: Presentations = {}
): Promise<void> {
const displaySetOptions = viewportInfo.getDisplaySetOptions();
@@ -383,10 +586,7 @@ class CornerstoneViewportService extends PubSubService implements IViewportServi
return viewport.setStack(imageIds, initialImageIndexToUse).then(() => {
viewport.setProperties({ ...properties });
const camera = presentations.positionPresentation?.camera;
if (camera) {
viewport.setCamera(camera);
}
this.setPresentations(viewport.id, presentations);
});
}
@@ -454,7 +654,7 @@ class CornerstoneViewportService extends PubSubService implements IViewportServi
viewport: Types.IVolumeViewport,
viewportData: VolumeViewportData,
viewportInfo: ViewportInfo,
presentations: Presentations
presentations: Presentations = {}
): Promise<void> {
// TODO: We need to overhaul the way data sources work so requests can be made
// async. I think we should follow the image loader pattern which is async and
@@ -558,7 +758,7 @@ class CornerstoneViewportService extends PubSubService implements IViewportServi
viewport.setProperties(properties, volumeId);
});
this.setPresentations(viewport, presentations);
this.setPresentations(viewport.id, presentations);
// load any secondary displaySets
const displaySetInstanceUIDs = this.viewportsDisplaySets.get(viewport.id);
@@ -712,7 +912,7 @@ class CornerstoneViewportService extends PubSubService implements IViewportServi
}
_setDisplaySets(
viewport: StackViewport | VolumeViewport,
viewport: Types.IViewport,
viewportData: StackViewportData | VolumeViewportData,
viewportInfo: ViewportInfo,
presentations: Presentations = {}
@@ -724,16 +924,18 @@ class CornerstoneViewportService extends PubSubService implements IViewportServi
viewportInfo,
presentations
);
} else if (viewport instanceof VolumeViewport || viewport instanceof VolumeViewport3D) {
}
if ([VolumeViewport, VolumeViewport3D].some(type => viewport instanceof type)) {
return this._setVolumeViewport(
viewport,
viewport as Types.IVolumeViewport,
viewportData as VolumeViewportData,
viewportInfo,
presentations
);
} else {
throw new Error('Unknown viewport type');
}
throw new Error('Unknown viewport type');
}
/**
@@ -763,8 +965,8 @@ class CornerstoneViewportService extends PubSubService implements IViewportServi
const { dimensions } = imageVolume;
const slabThickness = Math.sqrt(
dimensions[0] * dimensions[0] +
dimensions[1] * dimensions[1] +
dimensions[2] * dimensions[2]
dimensions[1] * dimensions[1] +
dimensions[2] * dimensions[2]
);
return slabThickness;
@@ -799,32 +1001,57 @@ class CornerstoneViewportService extends PubSubService implements IViewportServi
}
}
/**
* Looks through the viewports to see if the specified measurement can be
* displayed in one of the viewports.
*
* @param measurement
* The measurement that is desired to view.
* @param activeViewportId - the index that was active at the time the jump
* was initiated.
* @return the viewportId that the measurement should be displayed in.
*/
public getViewportIdToJump(
activeViewportId: string,
displaySetInstanceUID: string,
cameraProps: unknown
): string {
const viewportInfo = this.getViewportInfo(activeViewportId);
const { referencedImageId } = cameraProps;
if (viewportInfo?.contains(displaySetInstanceUID, referencedImageId)) {
return activeViewportId;
private enqueueViewportResizeRequest() {
this.resizeQueue.push(false); // false indicates viewport resize
clearTimeout(this.viewportResizeTimer);
this.viewportResizeTimer = setTimeout(() => {
this.processViewportResizeQueue();
}, this.gridResizeDelay);
}
private processViewportResizeQueue() {
const isGridResizeInQueue = this.resizeQueue.some(isGridResize => isGridResize);
if (this.resizeQueue.length > 0 && !isGridResizeInQueue && !this.gridResizeTimeOut) {
this.performResize();
}
return (
[...this.viewportsById.values()].find(viewportInfo =>
viewportInfo.contains(displaySetInstanceUID, referencedImageId)
)?.viewportId ?? null
);
// Clear the queue after processing viewport resizes
this.resizeQueue = [];
}
private performResize() {
const isImmediate = false;
const viewports = this.getRenderingEngine().getViewports();
// Store the current position presentations for each viewport.
viewports.forEach(({ id }) => {
const presentation = this.getPositionPresentation(id);
this.beforeResizePositionPresentations.set(id, presentation);
});
// Resize the rendering engine and render.
const renderingEngine = this.renderingEngine;
renderingEngine.resize(isImmediate);
renderingEngine.render();
// Reset the camera for viewports that should reset their camera on resize,
// which means only those viewports that have a zoom level of 1.
this.beforeResizePositionPresentations.forEach((positionPresentation, viewportId) => {
this.setPresentations(viewportId, { positionPresentation });
});
// Resize and render the rendering engine again.
renderingEngine.resize(isImmediate);
renderingEngine.render();
}
private resetGridResizeTimeout() {
clearTimeout(this.gridResizeTimeOut);
this.gridResizeTimeOut = setTimeout(() => {
this.gridResizeTimeOut = null;
}, this.gridResizeDelay);
}
}
@@ -1,6 +1,7 @@
import { Types } from '@cornerstonejs/core';
import { StackData, VolumeData } from '../../types/CornerstoneCacheService';
import { DisplaySetOptions, PublicViewportOptions, ViewportOptions } from './Viewport';
import { StackViewportData, VolumeViewportData } from '../../types/CornerstoneCacheService';
import { DisplaySetOptions, PublicViewportOptions } from './Viewport';
import { Presentations } from '../../types/Presentation';
/**
* Handles cornerstone viewport logic including enabling, disabling, and
@@ -36,7 +37,7 @@ export interface IViewportService {
* the element for resizing events
* @param {*} elementRef
*/
resize(element: HTMLDivElement): void;
resize(isGridResize: boolean): void;
/**
* Removes the viewport from cornerstone, and destroys the rendering engine
*/
@@ -55,8 +56,10 @@ export interface IViewportService {
* @returns
*/
setViewportData(
viewportData: StackData | VolumeData,
viewportId: string,
viewportData: StackViewportData | VolumeViewportData,
publicViewportOptions: PublicViewportOptions,
publicDisplaySetOptions: DisplaySetOptions[]
publicDisplaySetOptions: DisplaySetOptions[],
presentations?: Presentations
): void;
}
@@ -238,6 +238,11 @@ class ViewportInfo {
return this.viewportOptions;
}
public getPresentationIds(): CoreTypes.PresentationIds {
const { presentationIds } = this.viewportOptions;
return presentationIds;
}
public setDisplaySetOptions(displaySetOptions: Array<DisplaySetOptions>): void {
this.displaySetOptions = displaySetOptions;
}
@@ -1,23 +1,45 @@
/** Store presentation data for either stack viewports or volume viewports */
import { Types } from '@cornerstonejs/core';
import { Types as CoreTypes } from '@ohif/core';
import type { Types } from '@cornerstonejs/core';
/**
* Has information on the presentation of the viewport.
* Represents a position presentation in a viewport. This is basically
* viewport specific camera position and zoom, and not the display set
*/
export interface Presentation extends Types.StackViewportProperties {
presentationIds: CoreTypes.PresentationIds;
export type PositionPresentation = {
id: string;
viewportType: string;
initialImageIndex: number;
camera: Types.ICamera;
properties: Types.StackViewportProperties | Types.VolumeViewportProperties;
zoom?: number;
pan?: [number, number];
presentation: {
initialImageIndex: number;
viewUp: Types.Point3;
viewPlaneNormal: Types.Point3;
zoom?: number;
pan?: Types.Point2;
};
};
/**
* Represents a LUT presentation in a viewport, and is really related
* to displaySets and not the viewport itself. So that is why it can
* be an object with volumeId keys, or a single object with the properties
* itself
*/
export interface LutPresentation {
id: string;
viewportType: string;
presentation: Record<string, Types.ViewportProperties> | Types.ViewportProperties;
}
/**
* Presentation can be a PositionPresentation or a LutPresentation.
*/
type Presentation = PositionPresentation | LutPresentation;
/**
* Viewport presentations object that can contain a positionPresentation
* and or a lutPresentation.
*/
export type Presentations = {
positionPresentation?: Presentation;
lutPresentation?: Presentation;
positionPresentation?: PositionPresentation;
lutPresentation?: LutPresentation;
};
export default Presentation;
@@ -93,6 +93,7 @@ const CornerstoneViewportDownloadForm = ({
renderingEngine.resize();
// Trigger the render on the viewport to update the on screen
downloadViewport.resetCamera();
downloadViewport.render();
downloadViewportElement.addEventListener(
@@ -223,7 +224,6 @@ const CornerstoneViewportDownloadForm = ({
minimumSize={MINIMUM_SIZE}
maximumSize={MAX_TEXTURE_SIZE}
defaultSize={DEFAULT_SIZE}
canvasClass={'cornerstone-canvas'}
activeViewportElement={activeViewportElement}
enableViewport={enableViewport}
disableViewport={disableViewport}
+2
View File
@@ -23,6 +23,8 @@
"ohif-extension"
],
"scripts": {
"clean": "shx rm -rf dist",
"clean:deep": "yarn run clean && shx rm -rf node_modules",
"dev": "cross-env NODE_ENV=development webpack --config .webpack/webpack.dev.js --watch --output-pathinfo",
"dev:dicom-pdf": "yarn run dev",
"build": "cross-env NODE_ENV=production webpack --config .webpack/webpack.prod.js",
+2
View File
@@ -21,6 +21,8 @@
"yarn": ">=1.18.0"
},
"scripts": {
"clean": "shx rm -rf dist",
"clean:deep": "yarn run clean && shx rm -rf node_modules",
"dev": "cross-env NODE_ENV=development webpack --config .webpack/webpack.dev.js --watch --output-pathinfo",
"dev:dicom-pdf": "yarn run dev",
"build": "cross-env NODE_ENV=production webpack --config .webpack/webpack.prod.js",
+2
View File
@@ -20,6 +20,8 @@
"access": "public"
},
"scripts": {
"clean": "shx rm -rf dist",
"clean:deep": "yarn run clean && shx rm -rf node_modules",
"dev": "cross-env NODE_ENV=development webpack --config .webpack/webpack.dev.js --watch --output-pathinfo",
"build": "cross-env NODE_ENV=production webpack --config .webpack/webpack.prod.js",
"build:package-1": "yarn run build",
+2
View File
@@ -20,6 +20,8 @@
"access": "public"
},
"scripts": {
"clean": "shx rm -rf dist",
"clean:deep": "yarn run clean && shx rm -rf node_modules",
"dev": "cross-env NODE_ENV=development webpack --config .webpack/webpack.dev.js --watch --output-pathinfo",
"build": "cross-env NODE_ENV=production webpack --config .webpack/webpack.prod.js",
"build:package-1": "yarn run build",
@@ -23,6 +23,8 @@
"ohif-extension"
],
"scripts": {
"clean": "shx rm -rf dist",
"clean:deep": "yarn run clean && shx rm -rf node_modules",
"dev": "cross-env NODE_ENV=development webpack --config .webpack/webpack.dev.js --watch --output-pathinfo",
"dev:dicom-pdf": "yarn run dev",
"build": "cross-env NODE_ENV=production webpack --config .webpack/webpack.prod.js",
+2
View File
@@ -20,6 +20,8 @@
"access": "public"
},
"scripts": {
"clean": "shx rm -rf dist",
"clean:deep": "yarn run clean && shx rm -rf node_modules",
"dev": "cross-env NODE_ENV=development webpack --config .webpack/webpack.dev.js --watch --output-pathinfo",
"build": "cross-env NODE_ENV=production webpack --config .webpack/webpack.prod.js",
"build:package-1": "yarn run build",
+2
View File
@@ -20,6 +20,8 @@
"access": "public"
},
"scripts": {
"clean": "shx rm -rf dist",
"clean:deep": "yarn run clean && shx rm -rf node_modules",
"dev": "cross-env NODE_ENV=development webpack --config .webpack/webpack.dev.js --watch --output-pathinfo",
"build": "cross-env NODE_ENV=production webpack --config .webpack/webpack.prod.js",
"build:package": "yarn run build",